JSPM

  • Created
  • Published
  • Downloads 2685
  • Score
    100M100P100Q119231F
  • License LGPLv3

Client-side mongo database with server sync over http

Package Exports

  • minimongo

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (minimongo) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

Minimongo

A client-side MongoDB implementation which supports basic queries, including some geospatial ones.

Uses code from Meteor.js minimongo package, reworked to support more geospatial queries and made npm+browserify friendly. It was forked in January 2014.

It is either IndexedDb backed (IndexedDb), WebSQL backed (WebSQLDb), Local storage backed (LocalStorageDb) or in memory only (MemoryDb).

Autoselection is possible with utils.autoselectLocalDb(options, success, error)

Usage

Minimongo is designed to be used with browserify.

// Require minimongo
var minimongo = require("minimongo");

var LocalDb = minimongo.MemoryDb;

// Create local db (in memory database with no backing)
db = new LocalDb();

// Add a collection to the database
db.addCollection("animals");

doc = { species: "dog", name: "Bingo" };

// Always use upsert for both inserts and modifies
db.animals.upsert(doc, function() {
    // Success:

    // Query dog (with no query options beyond a selector)
    db.animals.findOne({ species:"dog" }, {}, function(res) {
        console.log("Dog's name is: " + res.name);
    });
});

Upserting

db.sometable.upsert(docs, bases, success, error) can take either a single document or multiple documents (array) for the first and second parameter.

docs is the document(s) to upsert. If bases is present, it is the base version on which the update is based. It can be omitted to use the current cached value as the base, or put as null to force an overwrite (a true upsert, not a patch)

Resolving upserts

Upserts are stored in local databases in a special state to record that they are upserts, not cached rows. The base document on which the upsert is based is also stored. For example, if a row starts in cached state with { x:1 } and is upserted to { x: 2 }, both the upserted and the original state are stored. This allows the server to do 3-way merging and apply only the changes.

To resolve the upsert (for example once sent to central db), use resolveUpserts on collection

db.sometable.resolveUpserts(upserts, success, error) takes the list of upserts to resolve

resolveUpserts does not resolve an upsert if another upsert on the same row has taken place. Instead, the base value is updated (since the change has been accepted by the server) but the new upserted value is left alone.

IndexedDb

To make a database backed by IndexedDb:

// Require minimongo
var minimongo = require("minimongo");

var IndexedDb = minimongo.IndexedDb;

// Create IndexedDb
db = new IndexedDb({namespace: "mydb"}, function() {
    // Add a collection to the database
    db.addCollection("animals", function() {
        doc = { species: "dog", name: "Bingo" };

        // Always use upsert for both inserts and modifies
        db.animals.upsert(doc, function() {
            // Success:

            // Query dog (with no query options beyond a selector)
            db.animals.findOne({ species:"dog" }, {}, function(res) {
                console.log("Dog's name is: " + res.name);
            });
        });
    });
}, function() { alert("some error!"); });

Caching

Rows can be cached without creating a pending upsert. This is done automatically when HybridDb uploads to a remote database with the returned upserted rows. It is also done when a query is performed on HybridDb: the results are cached in the local db and the query is re-performed on the local database.

The field _rev, if present is used to prevent overwriting with older versions. This is the odd scenario where an updated version of a row is present, but an older query to the server is delayed in returning. To prevent this race condition from giving stale data, the _rev field is used.

HybridDb

Queries the local database first and then returns remote data if different than local version.

This approach allows fast responses but with subsequent correction if the server has differing information.

The HybridDb collections can also be created in non-caching mode, which is useful for storing up changes to be sent to a sever:

db.addCollection("sometable", { caching: false })

To keep a local database and a remote database in sync, create a HybridDb:

hybridDb = new HybridDb(localDb, remoteDb)

Be sure to add the same collections to all three databases (local, hybrid and remote).

Then query the hybridDb (find and findOne) to have it get results and correctly combine them with any pending local results. If you are not interested in caching results, add { mode: "remote" } to the options of find or findOne

When upserts and removes are done on the HybridDb, they are queued up in the LocalDb until hybridDb.upload(success, error) is called.

upload will go through each collection and send any upserts or removes to the remoteDb.

RemoteDb

Uses AJAX-JSON calls to an API to query a real Mongo database. API is simple and contains only query, upsert, patch and remove commands.

If the client field is passed to the constructor, it is appended as a query parameters (e.g. ?client=1234) to each request made.

The API that RemoteDb should support four HTTP methods for each collection:

GET /<collection>

Performs a query, returning an array of results. GET query parameters are:

selector (optional) : JSON of query, in MongoDB format. e.g. {"a": 1} to find records with field a having value 1 fields (optional) : JSON object indicating which fields to return in MongoDB format. e.g. {"a": 1} to return only field a and _id sort (optional) : JSON of MongoDB sort field. e.g. ["a"] to sort ascending by a, or [["a","desc"]] to sort descending by a limit (optional) : Maximum records to return e.g. 100

Possible HTTP response codes:

200 : normal response 401 : client was invalid

POST /<collection>

Performs a single upsert, returning the upserted row. POST value is the document to upsert. Possible HTTP response codes:

200 : document was upserted. Returns the upserted object (see notes below on merging) 400 : document did not pass validation 401 : client was invalid or not present 403 : permission denied to upsert 409 : another client was upserting same document. Try again. 410 : document was already removed and cannot be upserted

On 403 or 410, the change is automatically discarded in the HybridDb.

PATCH /<collection>/<_id>

Performs a patch, returning the upserted row. PATCH value is the following structure:

{ 
    doc: <the document in its new form>
    base: <base document on which the changes were made>
}

For example, to change { x:1, y:1 } to set x to be 2, PATCH would send

{ 
    doc: { x:2, y: 1 }
    base: { x:1, y: 1 }
}

Possible HTTP response codes:

200 : document was upserted. Returns the upserted object 400 : document did not pass validation 401 : client was invalid or not present 403 : permission denied to upsert 409 : another client was upserting same document. Try again. 410 : document was already removed and cannot be upserted

On 403 or 410, the change is automatically discarded in the HybridDb.

DELETE /<collection>/<_id>

Removes a document. _id of the document to remove

200 : document was removed 401 : client was invalid or not present 403 : permission denied to remove 410 : document was already removed and cannot be removed again

On 403 or 410, the change is automatically discarded in the HybridDb.

Merging

Minimongo is designed to work with a server that performs three-way merging of documents that are being upserted by multiple users.

It can also be used with a simple server that just overwrites documents completely on upsert, just taking the doc value of PATCH, though this is not recommended.

Testing

To test, run testem in the main directory.

To test a RemoteDb implementation, use test/RemoteDbTests.coffee. Server must have a collection called scratch with fields as specified at top of tests file.