UNPKG

nqm-minimongo

Version:

Client-side mongo database with server sync over http

503 lines (475 loc) 14 kB
var Collection, IDBStore, IndexedDb, async, compileSort, processFind, utils, _; _ = require('lodash'); async = require('async'); IDBStore = require('idb-wrapper'); utils = require('./utils'); processFind = require('./utils').processFind; compileSort = require('./selector').compileSort; module.exports = IndexedDb = (function() { function IndexedDb(options, success, error) { var ex; this.collections = {}; try { this.store = new IDBStore({ dbVersion: 1, storeName: 'minimongo_' + options.namespace, keyPath: ['col', 'doc._id'], autoIncrement: false, onStoreReady: (function(_this) { return function() { if (success) { return success(_this); } }; })(this), onError: error, indexes: [ { name: 'col', keyPath: 'col', unique: false, multiEntry: false }, { name: 'col-state', keyPath: ['col', 'state'], unique: false, multiEntry: false } ] }); } catch (_error) { ex = _error; if (error) { error(ex); } return; } } IndexedDb.prototype.addCollection = function(name, success, error) { var collection; collection = new Collection(name, this.store); this[name] = collection; this.collections[name] = collection; if (success) { return success(); } }; IndexedDb.prototype.removeCollection = function(name, success, error) { delete this[name]; delete this.collections[name]; return this.store.query((function(_this) { return function(matches) { var keys; keys = _.map(matches, function(m) { return [m.col, m.doc._id]; }); if (keys.length > 0) { return _this.store.removeBatch(keys, function() { if (success != null) { return success(); } }, error); } else { if (success != null) { return success(); } } }; })(this), { index: "col", keyRange: this.store.makeKeyRange({ only: name }), onError: error }); }; return IndexedDb; })(); Collection = (function() { function Collection(name, store) { this.name = name; this.store = store; } Collection.prototype.find = function(selector, options) { return { fetch: (function(_this) { return function(success, error) { return _this._findFetch(selector, options, success, error); }; })(this) }; }; Collection.prototype.findOne = function(selector, options, success, error) { var _ref; if (_.isFunction(options)) { _ref = [{}, options, success], options = _ref[0], success = _ref[1], error = _ref[2]; } return this.find(selector, options).fetch(function(results) { if (success != null) { return success(results.length > 0 ? results[0] : null); } }, error); }; Collection.prototype._findFetch = function(selector, options, success, error) { return this.store.query(function(matches) { matches = _.filter(matches, function(m) { return m.state !== "removed"; }); if (success != null) { return success(processFind(_.pluck(matches, "doc"), selector, options)); } }, { index: "col", keyRange: this.store.makeKeyRange({ only: this.name }), onError: error }); }; Collection.prototype.upsert = function(docs, bases, success, error) { var items, keys, _ref; _ref = utils.regularizeUpsert(docs, bases, success, error), items = _ref[0], success = _ref[1], error = _ref[2]; keys = _.map(items, (function(_this) { return function(item) { return [_this.name, item.doc._id]; }; })(this)); return this.store.getBatch(keys, (function(_this) { return function(records) { var puts; puts = _.map(items, function(item, i) { var base; if (item.base !== void 0) { base = item.base; } else if (records[i] && records[i].doc && records[i].state === "cached") { base = records[i].doc; } else if (records[i] && records[i].doc && records[i].state === "upserted") { base = records[i].base; } else { base = null; } return { col: _this.name, state: "upserted", doc: item.doc, base: base }; }); return _this.store.putBatch(puts, function() { if (success) { return success(docs); } }, error); }; })(this), error); }; Collection.prototype.remove = function(id, success, error) { if (_.isObject(id)) { this.find(id).fetch((function(_this) { return function(rows) { return async.each(rows, function(row, cb) { return _this.remove(row._id, (function() { return cb(); }), cb); }, function() { return success(); }); }; })(this), error); return; } return this.store.get([this.name, id], (function(_this) { return function(record) { if (record == null) { record = { col: _this.name, doc: { _id: id } }; } record.state = "removed"; return _this.store.put(record, function() { if (success) { return success(id); } }, error); }; })(this)); }; Collection.prototype.cache = function(docs, selector, options, success, error) { var keys, puts, step2; step2 = (function(_this) { return function() { var docsMap, sort; docsMap = _.object(_.pluck(docs, "_id"), docs); if (options.sort) { sort = compileSort(options.sort); } return _this.find(selector, options).fetch(function(results) { var keys, removes; removes = []; keys = _.map(results, function(result) { return [_this.name, result._id]; }); if (keys.length === 0) { if (success != null) { success(); } return; } return _this.store.getBatch(keys, function(records) { var i, record, result, _i, _ref; for (i = _i = 0, _ref = records.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { record = records[i]; result = results[i]; if (!docsMap[result._id] && record && record.state === "cached") { if (options.sort && options.limit && docs.length === options.limit) { if (sort(result, _.last(docs)) >= 0) { continue; } } removes.push([_this.name, result._id]); } } if (removes.length > 0) { return _this.store.removeBatch(removes, function() { if (success != null) { return success(); } }, error); } else { if (success != null) { return success(); } } }, error); }, error); }; })(this); if (docs.length === 0) { return step2(); } keys = _.map(docs, (function(_this) { return function(doc) { return [_this.name, doc._id]; }; })(this)); puts = []; return this.store.getBatch(keys, (function(_this) { return function(records) { var doc, i, record, _i, _ref; for (i = _i = 0, _ref = records.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { record = records[i]; doc = docs[i]; if ((record == null) || record.state === "cached") { if (!record || !doc._rev || !record.doc._rev || doc._rev >= record.doc._rev) { puts.push({ col: _this.name, state: "cached", doc: doc }); } } } if (puts.length > 0) { return _this.store.putBatch(puts, step2, error); } else { return step2(); } }; })(this), error); }; Collection.prototype.pendingUpserts = function(success, error) { return this.store.query(function(matches) { var upserts; upserts = _.map(matches, function(m) { return { doc: m.doc, base: m.base || null }; }); if (success != null) { return success(upserts); } }, { index: "col-state", keyRange: this.store.makeKeyRange({ only: [this.name, "upserted"] }), onError: error }); }; Collection.prototype.pendingRemoves = function(success, error) { return this.store.query(function(matches) { if (success != null) { return success(_.pluck(_.pluck(matches, "doc"), "_id")); } }, { index: "col-state", keyRange: this.store.makeKeyRange({ only: [this.name, "removed"] }), onError: error }); }; Collection.prototype.resolveUpserts = function(upserts, success, error) { var keys; keys = _.map(upserts, (function(_this) { return function(upsert) { return [_this.name, upsert.doc._id]; }; })(this)); return this.store.getBatch(keys, (function(_this) { return function(records) { var i, puts, record, _i, _ref; puts = []; for (i = _i = 0, _ref = upserts.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { record = records[i]; if (record && record.state === "upserted") { if (_.isEqual(record.doc, upserts[i].doc)) { record.state = "cached"; puts.push(record); } else { record.base = upserts[i].doc; puts.push(record); } } } if (puts.length > 0) { return _this.store.putBatch(puts, function() { if (success) { return success(); } }, error); } else { if (success) { return success(); } } }; })(this), error); }; Collection.prototype.resolveRemove = function(id, success, error) { return this.store.get([this.name, id], (function(_this) { return function(record) { if (!record) { if (success != null) { success(); } return; } if (record.state === "removed") { return _this.store.remove([_this.name, id], function() { if (success != null) { return success(); } }, error); } }; })(this)); }; Collection.prototype.seed = function(docs, success, error) { var keys, puts; if (!_.isArray(docs)) { docs = [docs]; } keys = _.map(docs, (function(_this) { return function(doc) { return [_this.name, doc._id]; }; })(this)); puts = []; return this.store.getBatch(keys, (function(_this) { return function(records) { var doc, i, record, _i, _ref; for (i = _i = 0, _ref = records.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { record = records[i]; doc = docs[i]; if (record == null) { puts.push({ col: _this.name, state: "cached", doc: doc }); } } if (puts.length > 0) { return _this.store.putBatch(puts, function() { if (success != null) { return success(); } }, error); } else { if (success != null) { return success(); } } }; })(this), error); }; Collection.prototype.cacheOne = function(doc, success, error) { return this.store.get([this.name, doc._id], (function(_this) { return function(record) { if (record && doc._rev && record.doc._rev && doc._rev < record.doc._rev) { if (success != null) { success(); } return; } if (record == null) { record = { col: _this.name, state: "cached", doc: doc }; } if (record.state === "cached") { record.doc = doc; return _this.store.put(record, function() { if (success != null) { return success(); } }, error); } else { if (success != null) { return success(); } } }; })(this)); }; Collection.prototype.uncache = function(selector, success, error) { var compiledSelector; compiledSelector = utils.compileDocumentSelector(selector); return this.store.query((function(_this) { return function(matches) { var keys; matches = _.filter(matches, function(m) { return m.state === "cached" && compiledSelector(m.doc); }); keys = _.map(matches, function(m) { return [_this.name, m.doc._id]; }); if (keys.length > 0) { return _this.store.removeBatch(keys, function() { if (success != null) { return success(); } }, error); } else { if (success != null) { return success(); } } }; })(this), { index: "col", keyRange: this.store.makeKeyRange({ only: this.name }), onError: error }); }; return Collection; })();