# db8 JavaScript Wrapper API

The JavaScript wrapper to db8 (provided with the Foundation libraries) supports a basic set of database operations:

API | Description
--- | ---
del | Deletes objects from the database. Objects can be specified using a query or array of object IDs.
delKind | Deletes a JSON kind object from the database.
find | Finds objects matching a query.
get | Gets objects by ID. Returns array of complete objects. Fastest way to get data from DB.
merge | Updates properties in existing objects. Similar to SQL UPDATE. Objects can be specified using a query or array of object IDs.
put | Writes JSON data objects to db8 storage.
putKind | Puts a JSON kind object into the database. A kind object must be created before objects of that kind are stored.
reserveIds | Reserves a block of object IDs.

## Including the db8 JavaScript Wrapper in Your Code

A JavaScript wrapper is provided for calling the db8 service. It is part of the new Foundation JavaScript libraries.

To include the JavaScript wrapper for db8 API calls in your code:

1. Add the following lines to your app's index.html file:

       <script src="/usr/palm/frameworks/mojo/mojo.js" type="text/javascript" x-mojo-version="1"></script>
       <script src="/usr/palm/frameworks/mojoloader.js" type="text/javascript"></script>

2. Include the following lines of code in your assistant JS file:

       try {
             var libraries = MojoLoader.require({ name: "foundations", version: "1.0"      });
             var Future = libraries["foundations"].Control.Future; // Futures library
             var DB = libraries["foundations"].Data.DB;  // db8 wrapper library
          } catch (Error) {
          Mojo.Log.error(Error);
       }

   NOTE: The example calls in this documentation use a type of asynchronous callback known as a future. See future link for more information.

3. To code your JavaScript API calls, see the examples below.

## del

Deletes JSON data objects from the database.

Objects can be specified as a set of IDs (returns PutResponse) or as a db8 Query (returns CountResponse).

### Syntax

    del(idsOrQuery, purge);

### Parameters

Argument | Required | Type | Description
--- | --- | --- | ---
idsOrQuery | Yes | string array or Query | Array of JSON data object IDs or query.
purge | No | boolean | Mark as deleted or purge immediately flag. Default is false. If marked as deleted, records are still retrievable and are not fully deleted until an administrative purge occurs.

### Return

Response | Succcess: PutResponse (IDs) or CountResponse (query)

Examples

Example delete using ID

       var DB = libraries["foundations"].Data.DB; // Get JS wrapper for DB calls

       var ids = "2+Nd";  // Assign object ID

       DB.del([ids]).then(function(future) {
          var result = future.result;
          if (result.returnValue == true)
             Mojo.Log.info("del success, count="+result.count);
          else
          {
             result = future.exception;
             Mojo.Log.info("del failure: Err code=" + result.errorCode + "Err message=" + result.message);
          }
       });

Example delete using query

       var DB = libraries["foundations"].Data.DB;  // Get JS wrapper for DB calls

       //**
       //** Delete all records whose state value is equal to "OR" (Oregon)
       //** Note that without state being indexed, this call will fail with a
       //** "no index for query" message.
       //**
       var q = { "from":"com.mystuff.contacts:1", "where":[{"prop":"state","op":"=","val":"OR"}] };

       DB.del(q).then(function(future) {
          var result = future.result;
          if (result.returnValue === true)
             Mojo.Log.info("del success, count=" + result.count);
          else
          {
             result = future.exception;
             Mojo.Log.info("del failure: Err code=" + result.errorCode + "Err message=" + result.message);
          }
       });

## delKind

Deletes a kind object from the database. Deleting a kind deletes ALL data objects of that kind.

### Syntax

    delKind(id);

### Parameters

Argument | Required | Type | Description
--- | --- | --- | ---
id | Yes | string | Name of kind to delete.

### Return

Response | SuccessResponse

Example

       var DB = libraries["foundations"].Data.DB; // Get JS wrapper for DB calls
       //...
       DB.delKind("com.mystuff.contacts:1").then(function(future) {
          var result = future.result;
          if (result.returnValue === true)
             Mojo.Log.info("delKind success");
          else
          {
             result = future.exception;
             Mojo.Log.info("delKind failure: Err code=" + result.errorCode + "Err message=" + result.message);
          }
       });

## find

Returns a set of objects matching a query.

If your query is limited to a specified number of results, you can set a flag to get the number that would have been returned without a limit. You can also set a flag to request notification if any of the returned results from the query changes in the future.

### Syntax

    find(query, watch, count);

### Parameters

Argument | Required | Type | Description
--- | --- | --- | ---
query | Yes | Query | db8 query for retrieving results.
watch | Yes | boolean | Notify if change flag.
count | Yes | boolean | Return number of objects that would have been returned without the limit specified in the query flag.

### Return

Response | Succcess: FindResponse or NotificationResponse (watch == true)

Examples

Note: The examples below require the loading of the DB foundation libarary:

Example 1: Simple find

This query returns all data objects. No notification and no count is requested.

       var DB = libraries["foundations"].Data.DB; // Get JS wrapper for DB calls
       var fquery = {"from":"com.mystuff.contacts:1"};

       DB.find(fquery, false, false).then(function(future) {
          var result = future.result;
          if (result.returnValue === true)
             Mojo.Log.info("find success, results="+JSON.stringify(result.results));
          else
          {
             result = future.exception;
             Mojo.Log.info("find failure: Err code=" + result.errorCode + "Err message=" + result.message);
          }
    });

Example 2: Simple find with where clause

This query searches for objects whose 'displayName' field begins with a 'J'. All fields are returned. No notification and no count is requested.

       var DB = libraries["foundations"].Data.DB; // Get JS wrapper for DB calls
       var fquery = { "from":"com.mystuff.contacts:1","where":[{"prop":"displayName","op":"%","val":"J"}] };

       DB.find(fquery, false, false).then(function(future) {
          var result = future.result;
          if (result.returnValue == true)
             Mojo.Log.info("find success, results="+JSON.stringify(result.results));
          else
          {
             result = future.exception;
             Mojo.Log.info("find failure: Err code=" + result.errorCode + "Err message=" + result.message);
          }
    });

Example 3: Using select, order by, and desc

This query searches for contacts who live in California (state == 'CA'). The `displayName` and `state` fields are returned for each match. Returned objects are sorted on the `displayName` field in descending order. No notification is requested.

     //**
     //** Note: A dual index for the kind was created, allowing this call to work:
     //**          var indexes = [{"name":"dNameState", props:[{"name": "state"}, {"name":"displayName"}]} ];
     //**
     //**       The query below will not work if 'displayName' is declared as the first 'props' entry and 'state' the second.
     //**       Results can only be returned for contiguously ordered index entries. If you have the entries ["CA", "John Doe"], ["CA","Mabel Syrup"], etc.,
     //**       then, all of the entries with the same state are consecutive and only secondarily ordered by display name. Therefore, the index can answer the query.
     //**       If the index entries were ordered in ["displayName", "state"] order, the entries for the same state are not consecutive and
     //**       could not be used to answer the query (the query would fail with a "no index for query" message).

       var fquery = {"select": ["displayName", "state"],
                     "from":"com.mystuff.contacts:1",
                     "where":[{"prop":"state","op":"=","val":"CA"}],
                     "orderBy":"displayName",
                     "desc":true };

       DB.find(fquery, false, false).then(function(future) {
          var result = future.result;
          if (result.returnValue == true)
             Mojo.Log.info("find success, results="+JSON.stringify(result.results));
          else
          {
             result = future.exception;
             Mojo.Log.info("find failure: Err code=" + result.errorCode + "Err message=" + result.message);
          }
       });

Example 4: Query with change notification

This example shows how change notiification for a find works. Initially, a find with change notification (watch=true) is issued on a non-existent Kind and DB. Empty results are then returned. A number of operations are done that alter the results which fires the find's change notification flag. Each time, the find with a watch is re-issued and the new results are printed out.

       //** Create Kind object for test
       var testKind = {
       name: "dbtest:1",
       owner: "com.palm.dbtest",
       indexes: [
          {name:"name", props: [{name: "name"}]},
          {name:"profession", props:[{name: "profession"}]}
          ]
       };

       //** Create 5 test data objects
       var testData =  [
       {
          _kind: testKind.name,
          name: "Mark",
          age: 40,
          profession: "engineer"
       },
       {
          _kind: testKind.name,
          name: "Yvette",
          age: 36,
          profession: "trainer"
       },
       {
           _kind: testKind.name,
          name: "Lenny",
          age: 45,
          profession: "engineer"
       },
       {
          _kind: testKind.name,
          name: "Homer",
          age: 51,
          profession: "safety inspector"
       },
       {
          _kind: testKind.name,
          name: "Marge",
          age: 48,
          profession: "homemaker"
       }
       ];

       //** Create query object for test
       var query = {
          select: ["name", "age"],
          from: testKind.name,
          where: [{prop: "profession", op: "=", val: "engineer"}],
          desc: false,   //** descending
          incDel: false, //** include deleted
          limit: 500
       };

       //** Do find on non-existent Kind with watch. No results initially returned.
       DB.find({from:testKind.name}, true, 500).then(function print_it(future) {

       //** Notification that results have changed
       if (future.result.fired) {
          Mojo.Log.info("DB.find watch fired, re-issuing 'find'");

          //** Re-issue find to get changed results
          return DB.find({from:testKind.name}, true, 500).then(print_it);

       }
       else {
           //** Log find results
           Mojo.Log.info("DB.find Database contents: "+JSON.stringify(future.result));
       }

          //** Re-establish future
          future.then(print_it);
          },
          function error(future) { //** Log error
             Mojo.Log.info("error: "+future.exception);
       });

       //** Create test Kind in database
       DB.putKind(testKind.name, testKind.owner, testKind.indexes).then(function(future) {
          Mojo.Log.info("putKind returned "+JSON.stringify(future.result));

       return DB.put(testData); //** Put test data into database

       }).then(function(future){
          // Log put results
          Mojo.Log.info("put returned "+JSON.stringify(future.result));
          var changes = {
               profession: "janitor"
           };

       return DB.merge(query, changes); // Change all professions to janitor
       }).then(function(future){
          // Log merge results
          Mojo.Log.info("merge returned "+JSON.stringify(future.result));
          var delQuery = {
           from: testKind.name
       };

       return DB.del(delQuery);  // Delete all records in DB
       }).then(function(future){
          //** Log delete results
          Mojo.Log.info("delete returned "+JSON.stringify(future.result));
          return true;
       }).then(function(future){
       return;
    });

When this code is run, the following results (without comments) are logged, showing the sequence of events:

       //** Initial find results on empty DB
          com.palm.dbtest: Info: DB.find Database contents: {"returnValue":true,"results":[],"count":0},

       //** Kind stored in DB
          com.palm.dbtest: Info: putKind returned {"returnValue":true},

       //** Put fires find watch, find re-issued
          com.palm.dbtest: Info: DB.find watch fired, re-issuing find,

       //** Results from put
          com.palm.dbtest: Info: put returned {"returnValue":true,"results":[{"id":"20RH","rev":5906},{"id":"20RI","rev":5907},{"id":"20RJ","rev":5908},{"id":"20RK","rev":5909},{"id":"20RL","rev":5910}]},

       //** First reissued find results returned
          com.palm.dbtest: Info: DB.find Database contents: {"returnValue":true,"results":[{"_id":"20RH","_kind":"dbtest:1","_rev":5906,"age":40,"name":"Mark","profession":"engineer"},{"_id":"20RI","_kind":"dbtest:1","_rev":5907,"age":36,"name":"Yvette","profession":"trainer"},{"_id":"20RJ","_kind":"dbtest:1","_rev":5908,"age":45,"name":"Lenny","profession":"engineer"},{"_id":"20RK","_kind":"dbtest:1","_rev":5909,"age":51,"name":"Homer","profession":"safety inspector"},{"_id":"20RL","_kind":"dbtest:1","_rev":5910,"age":48,"name":"Marge","profession":"homemaker"}],"count":5},

       //** Merge fires re-issued find
          com.palm.dbtest: Info: DB.find watch fired, re-issuing find,

       //** Merge results returned
          com.palm.dbtest: Info: merge returned {"returnValue":true,"count":2},

       //** Second re-issued find results returned
          com.palm.dbtest: Info: DB.find Database contents: {"returnValue":true,"results":[{"_id":"20RH","_kind":"dbtest:1","_rev":5911,"age":40,"name":"Mark","profession":"janitor"},{"_id":"20RI","_kind":"dbtest:1","_rev":5907,"age":36,"name":"Yvette","profession":"trainer"},{"_id":"20RJ","_kind":"dbtest:1","_rev":5912,"age":45,"name":"Lenny","profession":"janitor"},{"_id":"20RK","_kind":"dbtest:1","_rev":5909,"age":51,"name":"Homer","profession":"safety inspector"},{"_id":"20RL","_kind":"dbtest:1","_rev":5910,"age":48,"name":"Marge","profession":"homemaker"}],"count":5},

       //** Delete fires re-issued find
          com.palm.dbtest: Info: DB.find watch fired, re-issuing find,

       //** Delete results returned
          com.palm.dbtest: Info: delete returned {"returnValue":true,"count":5},

       //** Third re-issued find results returned. DB now empty again
          com.palm.dbtest: Info: DB.find Database contents: {"returnValue":true,"results":[],"count":0},

Example 4: Query showing use of paging

Query to get 20 contacts, then use page key from first query to get 20 more contacts.

       //** Create query to get 20 contacts who live in California
       var fquery = {"from":"com.mystuff.contacts:1",
                    "where":[{"prop":"state","op":"=","val":"CA"}],
                    "limit": 20};

       DB.find(fquery, false, true).then(function(future) {
           var result = future.result;
           if (result.returnValue == true)
           {
              Mojo.Log.info("First page results, count = "+result.count);
              var rs = result.results;

              for (var i=0; i < rs.length; i++)
                 Mojo.Log.info("#"+i+" "+ rs[i].name[0].familyName+", "+rs[i].name[0].givenName);

        //** ...
        //** Do something, i.e., wait for user to press next key
        //** ...

       //**
       //** Create query to get next 20 contacts. Use next page key returned from previous query
       //**
        fquery = {  "from":"com.mystuff.contacts:1",
                    "where":[{"prop":"state","op":"=","val":"CA"}],
                    "limit": 20,
                    "page":result.next};
        //**
        //** Get next page of results
        //**
        DB.find(fquery, false, true).then(function(future) {
             var result = future.result;
             if (result.returnValue === true)
             {
                Mojo.Log.info("Next page results, count = "+result.count);
                var rs = result.results;
                for (var i=0; i < rs.length; i++)
                   Mojo.Log.info("#"+i+" "+ rs[i].name[0].familyName+", "+rs[i].name[0].givenName);
             }
             else
             {
                result = future.exception;
                Mojo.Log.info("Next find failure: Err code=" + result.errorCode + "Err message=" + result.message);
             }
       });

      }
      else
         Mojo.Log.error("First page failure: Err code=" + result.errorCode + "Err text=" + result.errorText);
    });

## get

Gets JSON data objects by IDs.

### Syntax

    get(ids);

### Parameters

Argument | Required | Type | Description
--- | --- | --- | ---
ids | Yes | string array | Array of JSON data object IDs.

### Return

Response | GetResponse

Example

       //** Get JS wrapper for DB calls
       var DB = libraries["foundations"].Data.DB;
       //...
       var ids = ["2+OG", "2+OK"];
       DB.get(ids).then(function(future) {
          var result = future.result;
          var rs = future.result.results;
          if (result.returnValue == true)
          {
              for (var i=0; i < result.results.length; i++)
              {
                  Mojo.Log.info("get success, id="+ rs[i]._id+", kind="+rs[i]._kind+", displayName="+rs[i].displayName+", nickname="+rs[i].nickname+", familyName="+rs[i].name[0].familyName);
              }
          }
          else
          {
             result = future.exception;
             Mojo.Log.info("get failure: Err code=" + result.errorCode + "Err message=" + result.message);
          }
       });

## merge

Updates properties in existing objects. Objects can be specified using query. (returns CountResponse) or array of IDs.

### Syntax

    merge(objectsOrQuery, properties);

### Parameters

Argument | Required | Type | Description
--- | --- | --- | ---
objectsOrQuery | Yes | any | array of IDs and properties to update, or db8 Query
properties | No | any | Array of name/value pairs to update (if using db8 Query).

### Return

Response | Succcess: PutResponse (IDs), or CountResponse (query)

Examples

Example using IDs

       var DB = libraries["foundations"].Data.DB;  // Get JS wrapper for DB calls

       //** Create objects to update nickname field
       var m1 = {  "_id": "2+SF", "nickname": "Spaz" };
       var m2 = {  "_id": "2+SJ","nickname": "Sugar" };

       var objs2 = [m1, m2];
       DB.merge(objs2).then(function(future) {
          var result = future.result;
          var rs = result.results;
          if (result.returnValue === true)
          {
             Mojo.Log.info("merge success");
             for (var i=0; i < rs.length; i++)
                Mojo.Log.info("#"+i+": r.id="+rs[i].id+", r.rev="+rs[i].rev);
          }
          else
          {
             result = future.exception;
             Mojo.Log.info("merge failure: Err code=" + result.errorCode + "Err message=" + result.message);
          }
       });

Example using query

       //**
       //** Assign a property to update, and create the query
       //** Note that indexes must exist for the fields you are querying on
       //**
       var mprops = { "nickname":"Spud"};
       var mquery = { "from":"com.mystuff.contacts:1","where":[{"prop":"displayName","op":"%","val":"J"}] };

       DB.merge(mquery, mprops).then(function(future) {
          var result = future.result;
          if (result.returnValue === true)
             Mojo.Log.info("merge success, number updated = "+result.count);
          else
          {
             result = future.exception;
             Mojo.Log.info("merge failure: Err code=" + result.errorCode + "Err message=" + result.message);
          }
       });

## put

Puts JSON data objects of a particular kind into the database. Assigns id field if not set. Returns id and rev for each object stored.

### Syntax

    put(objs);

### Parameters

Argument | Required | Type | Description
--- | --- | --- | ---
objs | Yes | any object array | Array of JSON data objects.

### Return

Response | PutResponse

Example

       var DB = libraries["foundations"].Data.DB;  // Get JS wrapper for DB calls

       //** Create new JSON data object
       var  contact1 = {
          _kind: "com.mystuff.contacts:1",
          displayName: "John Doe",
          name: [{"familyName":"Doe", "givenName":"John"}],
          nickname: "Skippy",
          emails:[
            {"value":"jdoe@gmail.com", "type":"home"},
            {"value": "john.doe@palm.com", "type":"work"}]};

       //** Create object array
       var objs = [contact1];

       DB.put(objs).then(function(future) {
          var result = future.result;
          if (result.returnValue === true)
             Mojo.Log.info("put success, c.id="+result.results[0].id+", c.rev="+result.results[0].rev);
          else
          {
             result = future.exception;
             Mojo.Log.info("put failure: Err code=" + result.errorCode + "Err message=" + result.message);
          }
       });

## putKind

Register a kind JSON object with the database.

Kind objects define the indexes for a JSON data object.

Your index can be composed of multiple fields. When you create your index, be aware that queries can only return results that are contiguously indexed. See All Queries Must be on Indexed Fields for more information.

If your app or service creates objects that other apps or services need to access, then see Allowing Other Apps or Services to Access Your db8 Data for more information.

### Syntax

    putKind(id, owner, indexes);

### Parameters

Argument | Required | Type | Description
--- | --- | --- | ---
id | Yes | string | Kind identifier.
owner | Yes | string | Kind owner - Service's bus address or app's app ID. Only the owner has permission to modify the kind.
indexes | No | IndexClause array | Kind indexes.

### Return

Response | Succcess: SuccessResponse Failure: ErrResponse

Example

       var DB = libraries["foundations"].Data.DB;  // Get JS wrapper for DB calls
       var indexes = [{"name":"state", props:[{"name": "state"}]}];
       DB.putKind("com.palm.contacts:1", "com.palm.contacts", indexes).then(function(future) {
          var result = future.result;
          if (result.returnValue === true)
             Mojo.Log.info("putKind success");
          else
          {
             result = future.exception;
             Mojo.Log.info("putKind failure: Err code=" + result.errorCode + "Err message=" + result.message);
          }
       });

## reserveIds

Reserves a block of object ids.

### Syntax

    reserveIds(count);

### Parameters

Argument | Required | Type | Description
--- | --- | --- | ---
count | Yes | integer | Number of Ids to reserve.

### Return

Response | Succcess: SuccessResponse Failure: ErrResponse

Example

Get block of 10 IDs:

        var DB = libraries["foundations"].Data.DB;  //  Get JS wrapper for DB calls

        DB.reserveIds(10).then(function(future) {
           var result = future.result;
           if (result.returnValue === true)
              Mojo.Log.info("reserveIds success, results="+JSON.stringify(result.ids));
          else
          {
             result = future.exception;
             Mojo.Log.info("reserveIds failure: Err code=" + result.errorCode + "Err message=" + result.message);
          }
       });