A Datastore
object manages a set of JSON documents, keyed by ID.
The DatastoreManager
module manages a directory where Datastore
objects
store their data. It's a factory for named Datastore
instances. A
named datastore will persist its data between application runs. Names are
arbitrary strings, with the restriction that the name must match
^[a-zA-Z]+[a-zA-Z0-9_]*
.
The DatastoreManager
directory defaults to CloudantSync
within the
cordova.file.dataDirectory
unless the path option is specified. This directory
contains simple folders and SQLite databases if you want to take a peek.
Therefore, start by requiring the DatastoreManager
module to manage datastores for
that given directory:
var DatastoreManager = cordova.require('cloudant-sync.DatastoreManager');
Once you've required the DatastoreManager
, it's straightforward to create Datastores:
var datastoreManager;
var datastore;
DatastoreManager.DatastoreManager()
.then(function(my_datastoreManager) {
datastoreManager = my_datastoreManager;
return datastoreManager.openDatastore('my_datastore');
})
.then(function(my_datastore) {
// do something with my_datastore e.g.
datastore = my_datastore;
// return a promise for opening other_datastore to chain
return datastoreManager.openDatastore('other_datastore');
})
.then(function (other_datastore) {
// do something with other_datastore
})
.done();
The DatastoreManager
handles creating and initialising non-existent
datastores, so the object returned is ready for reading and writing.
To delete a datastore:
datastoreManager.deleteDatastore("my_datastore")
.then(function () {
// my_datastore successfully deleted
});
It's important to note that this doesn't check there are any active
Datastore
objects for this datastore. The behaviour of active Datastore
objects after their underlying files have been deleted is undefined.
Once you have a Datastore
instance, you can use it to create, update and
delete documents.
Documents are represented as a set of revisions. To create a document, you set up the initial revision of the document and save that to the datastore.
Create a mutable document revision object, set its body, ID and attachments
and then call createDocumentFromRevision(documentRevision)
to add it to the datastore:
// Create a document
var rev = {};
rev._id = "doc1"; // Or don't assign the _id property, we'll generate one
// Build up body content
rev.description = 'Buy milk';
rev.completed = false;
rev.type = 'cloudant-sync.example.task';
datastore.createDocumentFromRevision(rev)
.then(function (savedRevision) {
// do something with savedRevision
}).done();
Once you have created one or more documents, retrieve them by ID:
var docId = revision._id;
datastore.getDocument(docId)
.then(function (fetchedRevision) {
// do something with fetchedRevision
}).done();
To update a document, make your changes on the most recent revision and save the document:
fetchedRevision.completed = true;
datastore.updateDocumentFromRevision(fetchedRevision)
.then(function (updatedRevision) {
// do something with updatedRevision
}).done();
To delete a document, you need the current revision:
datastore.deleteDocumentFromRevision(updatedRevision)
.then(function (deletedRevision) {
// do something with updatedRevision
}).done();
You don't need to know the ID of the document to retrieve it. Datastore provides ways to index and search the fields of your JSON documents. For more, see index-query.md.
You can associate attachments with the JSON documents in your datastores. Attachments are blobs of binary data, such as photos or short sound snippets. They should be of small size -- maximum a few MB -- because they are replicated to and from the server in a way which doesn't allow for resuming an upload or download.
Attachments are stored in the _attachments
property of a document revision
object. This is a map of attachments, keyed by attachment name.
To add an attachment to a document, just add (or overwrite) the attachment
in _attachments
:
// Create a new document or get an existing one
var rev = {};
// As with the document body, you can replace the attachments
rev._attachments = {};
// Or just add or update a single one:
rev._attachments['cute_cat.jpg'] = {
content_type: 'image/jpeg',
data: "Attachment data must be a Base64 encoded String"
};
datastore.createDocumentFromRevision(rev)
.then(function (savedRevision) {
// do something with saved revision
}).done();
To read an attachment, hydrate the Base64 encoded 'data' value for the desired attachment in the _attachments
map.
var data = savedRevision._attachments['cute_cat.jpg'].data;
To remove an attachment, remove it from the _attachments
map:
delete savedRevision._attachments['cute_cat.jpg'];
To remove all attachments, delete the _attachments
property or set it to an empty map
or null
:
savedRevision._attachments = null;
This section shows all the ways (that I could think of) that you can update, modify and delete documents.
This is the simplest case as we don't need to worry about previous revisions.
-
Add a document with body, but not attachments or ID. You'll get an autogenerated ID.
var rev = { foo: 'bar' }; datastore.createDocumentFromRevision(rev) .then(function (savedRevision) { // do something with savedRevision }).done();
-
Add a new document to the store with a body and ID, but without attachments.
var rev = { _id: 'doc1', foo: 'bar' }; datastore.createDocumentFromRevision(rev) .then(function (savedRevision) { // do something with savedRevision }).done();
-
Add a new document to the store with attachments.
var rev = { _id: 'doc1', foo: 'bar', _attachments: { 'image.jpg': { content_type: 'image/jpeg', data: "Attachment data must be a Base64 encoded String" } } }; datastore.createDocumentFromRevision(rev) .then(function (savedRevision) { // do something with savedRevision }).done();
-
Add a document with body and attachments, but no ID. You'll get an autogenerated ID.
var rev = { foo: 'bar', _attachments: { 'image.jpg': { content_type: 'image/jpeg', data: "Attachment data must be a Base64 encoded String" } } }; datastore.createDocumentFromRevision(rev) .then(function (savedRevision) { // do something with savedRevision }).done();
To update a document, make your changes to the winning revision and save the document.
For the first set of examples the original document is set up with a body and no attachments:
var rev = {
_id: 'doc1',
foo: 'bar'
};
datastore.createDocumentFromRevision(rev)
.then(function (savedRevision) {
// do something with savedRevision
}).done();
We also assume an attachment ready to be added:
var att1 = {
content_type: 'image/jpeg',
data: base64EncodedString
}
-
Update body for doc that has no attachments, adding no attachments
savedRevision.bar = 'foo'; datastore.updateDocumentFromRevision(savedRevision) .then(function (updatedRevision) { // do something with updated revision }).done();
-
Update body for doc with no attachments, adding attachments. Here we see that a mutableCopy of a document with no attachments has an
HashMap
set for itsattachments
property.savedRevision.bar = 'foo'; savedRevision._attachments = { 'image.jpg': att1 }; datastore.updateDocumentFromRevision(savedRevision) .then(function (updatedRevision) { // do something with updated revision }).done();
-
Update body for doc with no attachments, removing attachments dictionary entirely.
savedRevision.bar = 'foo'; savedRevision._attachments = null; datastore.updateDocumentFromRevision(savedRevision) .then(function (updatedRevision) { // do something with updated revision }).done();
-
Update the attachments without changing the body, add attachments to a doc that had none.
savedRevision._attachments = { 'image.jpg': att1 }; datastore.updateDocumentFromRevision(savedRevision) .then(function (updatedRevision) { // do something with updated revision }).done();
-
Update attachments by copying from another revision.
savedRevision._attachments = anotherDoc._attachments; datastore.updateDocumentFromRevision(savedRevision) .then(function (updatedRevision) { // do something with updated revision }).done();
-
Updating a document using an outdated source revision causes a conflict
// Note: clone() represents a function that makes a deep copy of the object var update1 = clone(savedRevision); var update2 = clone(savedRevision); udpate1.bar = 'foo'; update2.bar = 'foobar'; datastore.updateDocumentFromRevision(update1) .then(function (updatedRevision) { return datastore.updateDocumentFromRevision(update2); }) .catch(function (error) { // Returns a conflict error }).done();
For the second set of examples the original document is set up with a body and several attachments:
var rev = {
_id: 'doc1',
food: 'bar',
_attachments:{
att1: att1,
att2: att2,
att3, att3
}
};
datastore.createDocumentFromRevision(rev)
.then(function (savedRevision) {
}).done();
-
Update body without changing attachments
savedRevision.bar = 'foo'; datastore.updateDocumentFromRevision(savedRevision) .then(function (updatedRevision) { // Should have the same attachments }).done();
-
Update the attachments without changing the body, remove attachments
delete savedRevision._attachments.att1; datastore.updateDocumentFromRevision(savedRevision) .then(function (updatedRevision) { }).done();
-
Update the attachments without changing the body, add attachments
// Create att100 attachment savedRevision._attachments.att100 = att100; datastore.updateDocumentFromRevision(savedRevision) .then(function (updatedRevision) { }).done();
-
Update the attachments without changing the body, remove all attachments by setting
null
for attachments map.savedRevision._attachments = null; datastore.updateDocumentFromRevision(savedRevision) .then(function (updatedRevision) { }).done();
-
Update the attachments without changing the body, remove all attachments by setting an empty object.
savedRevision._attachments = {}; datastore.updateDocumentFromRevision(savedRevision) .then(function (updatedRevision) { }).done();
-
Copy an attachment from one document to another.
// Note: clone() represents a function that makes a deep copy of the object var attCopy = clone(anotherRevision._attachments['nameOfAttachment']); MutableDocumentRevision rev = new MutableDocumentRevision(); // Add copy of attachment to "savedRevision" savedRevision._attachments['nameOfAttachment'] = attCopy; datastore.updateDocumentFromRevision(savedRevision) .then(function (updatedRevision) { }).done();
-
You should be able to delete a given revision (i.e., add a tombstone to the end of the branch).
datastore.getDocument("doc1") .then(function (fetchedRevision) { return datastore.deleteDocumentFromRevision(fetchedRevision); }) .then(function (deletedRevision) { }).done();
This would refuse to delete if
fetchedRevision
was not a leaf node.