Foundations.Data.DB
The JavaScript wrapper to db8 (provided with the Foundation libraries) supports a basic set of database operations.
Using Foundations.Data.DB
To use Foundations.Data.DB:
-
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>
-
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.
-
To code your JavaScript API calls, see the examples below.
DB Functions
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. |
DB.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);
}
});
DB.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);
}
});
DB.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);
});
DB.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);
}
});
DB.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);
}
});
DB.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);
}
});
DB.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);
}
});
DB.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);
}
});