UNPKG

mobdb

Version:

MarsDB is a lightweight client-side MongoDB-like database, Promise based, written in ES6

206 lines (189 loc) 5.41 kB
import _bind from 'fast.js/function/bind'; import _keys from 'fast.js/object/keys'; import _each from 'fast.js/forEach'; import _map from 'fast.js/map'; import invariant from 'invariant'; import PromiseQueue from './PromiseQueue'; import CollectionIndex from './CollectionIndex'; import DocumentRetriver from './DocumentRetriver'; /** * Manager for controlling a list of indexes * for some model. Building indexes is promise * based. * By default it creates an index for `_id` field. */ /* istanbul ignore next */ export class IndexManager { constructor(db, options = {}) { this.db = db; this.indexes = {}; this._queue = new PromiseQueue(options.concurrency || 2); // By default ensure index by _id field this.ensureIndex({ fieldName: '_id', unique: true, }); } /** * Check index existance for given `options.fieldName` and * if index not exists it creates new one. * Always return a promise that resolved only when * index succesfully created, built and ready for working with. * If `options.forceRebuild` provided and equals to true then * existing index will be rebuilt, otherwise existing index * don't touched. * * @param {Object} options.fieldName name of the field for indexing * @param {Object} options.forceRebuild rebuild index if it exists * @return {Promise} */ ensureIndex(options) { invariant( options && options.fieldName, 'You must specify a fieldName in options object' ); const key = options.fieldName; if (!this.indexes[key]) { this.indexes[key] = new CollectionIndex(options); return this.buildIndex(key); } else if (this.indexes[key].buildPromise) { return this.indexes[key].buildPromise; } else if (options && options.forceRebuild) { return this.buildIndex(key); } else { return Promise.resolve(); } } /** * Buld an existing index (ensured) and return a * promise that will be resolved only when index successfully * built for all documents in the storage. * @param {String} key * @return {Promise} */ buildIndex(key) { invariant( this.indexes[key], 'Index with key `%s` does not ensured yet', key ); console.log("buildIndex", key); const cleanup = () => { this.indexes[key].buildPromise = null }; const buildPromise = this._queue.add( _bind(this._doBuildIndex, this, key) ).then(cleanup, cleanup); this.indexes[key].buildPromise = buildPromise; return buildPromise; } /** * Schedule a task for each index in the * manager. Return promise that will be resolved * when all indexes is successfully built. * @return {Promise} */ buildAllIndexes() { return Promise.all( _map(this.indexes, (v, k) => { return this.ensureIndex({ fieldName: k, forceRebuild: true, }); }) ); } /** * Remove an index * @param {String} key * @return {Promise} */ removeIndex(key) { return this._queue.add(() => { delete this.indexes[key]; }); } /** * Add a document to all indexes * @param {Object} doc * @return {Promise} */ indexDocument(doc) { return this._queue.add(() => { const keys = _keys(this.indexes); let failingIndex = null; try { _each(keys, (k, i) => { failingIndex = i; this.indexes[k].insert(doc); }); } catch (e) { _each(keys.slice(0, failingIndex), (k) => { this.indexes[k].remove(doc); }); throw e; } }); } /** * Update all indexes with new version of * the document * @param {Object} oldDoc * @param {Object} newDoc * @return {Promise} */ reindexDocument(oldDoc, newDoc) { return this._queue.add(() => { const keys = _keys(this.indexes); let failingIndex = null; try { _each(keys, (k, i) => { failingIndex = i; this.indexes[k].update(oldDoc, newDoc); }); } catch (e) { _each(keys.slice(0, failingIndex), (k) => { this.indexes[k].revertUpdate(oldDoc, newDoc); }); throw e; } }); } /** * Remove document from all indexes * @param {Object} doc * @return {Promise} */ deindexDocument(doc) { return this._queue.add(() => { const keys = _keys(this.indexes); _each(keys, (k) => { this.indexes[k].remove(doc); }); }); } /** * Build an existing index with reseting first * @param {String} key * @return {Promise} */ _doBuildIndex(key) { // Get and reset index const index = this.indexes[key]; index.reset(); // Loop through all doucments in the storage const errors = []; return new DocumentRetriver(this.db) .retriveAll().then((docs) => { _each(docs, doc => { try { index.insert(doc); } catch (e) { errors.push([e, doc]); } }); if (errors.length > 0) { throw new Error('Index build failed with errors: ', errors); } }); } } export default IndexManager;