diff --git a/lib/datastore.js b/lib/datastore.js index cee6f09..d35f81b 100644 --- a/lib/datastore.js +++ b/lib/datastore.js @@ -42,22 +42,25 @@ Datastore.prototype.resetIndexes = function (newData) { /** * Ensure an index is kept for this field. Same parameters as lib/indexes * For now this function is synchronous, we need to test how much time it takes + * We use an async API for consistency with the rest of the code * @param {String} options.fieldName * @param {Boolean} options.unique * @param {Boolean} options.sparse - * @return {Boolean} true if index was created or already exists, false otherwise + * @param {Function} cb Optional callback, signature: err */ -Datastore.prototype.ensureIndex = function (options) { +Datastore.prototype.ensureIndex = function (options, cb) { + var callback = cb || function () {}; + options = options || {}; - if (!options.fieldName) { return false; } - if (this.indexes[options.fieldName]) { return true; } + if (!options.fieldName) { return callback({ missingFieldName: true }); } + if (this.indexes[options.fieldName]) { return callback(); } options.datastore = this; this.indexes[options.fieldName] = new Index(options); this.indexes[options.fieldName].insert(this.data); - return true; + return callback(); }; @@ -106,7 +109,7 @@ Datastore.prototype.removeFromIndexes = function (doc) { /** * Update a document in all indexes */ -Datastore.prototype.removeFromIndexes = function (doc, newDoc) { +Datastore.prototype.updateIndexes = function (doc, newDoc) { var self = this; Object.keys(this.indexes).forEach(function (i) { diff --git a/lib/indexes.js b/lib/indexes.js index 8022fac..39bd5c8 100644 --- a/lib/indexes.js +++ b/lib/indexes.js @@ -56,7 +56,7 @@ Index.prototype.reset = function (newData) { Index.prototype.insert = function (doc) { var key, self = this; - if (util.isArray(doc)) { doc.forEach(function (d) { self.insert(d); }); return; } + if (util.isArray(doc)) { this.insertMultipleDocs(doc); return; } key = model.getDotValue(doc, this.fieldName); @@ -70,8 +70,36 @@ Index.prototype.insert = function (doc) { }; +/** + * When inserting an array of documents, we need to rollback all insertions + * if an error is thrown + */ +Index.prototype.insertMultipleDocs = function (docs) { + var i, error, failingI; + + for (i = 0; i < docs.length; i += 1) { + try { + this.insert(docs[i]); + } catch (e) { + error = e; + failingI = i; + break; + } + } + + if (error) { + for (i = 0; i < failingI; i += 1) { + this.remove(docs[i]); + } + + throw error; + } +}; + + /** * Remove a document from the index + * If an array is passed, we remove all its elements * O(log(n)) */ Index.prototype.remove = function (doc) { diff --git a/test/indexes.test.js b/test/indexes.test.js index 4d2d3aa..16f62f5 100644 --- a/test/indexes.test.js +++ b/test/indexes.test.js @@ -105,6 +105,25 @@ describe('Indexes', function () { assert.deepEqual(idx.tree.search('bloup'), [doc3]); }); + it('When inserting an array of elements, if an error is thrown all inserts need to be rolled back', function () { + var idx = new Index({ fieldName: 'tf', unique: true }) + , doc1 = { a: 5, tf: 'hello' } + , doc2 = { a: 8, tf: 'world' } + , doc2b = { a: 84, tf: 'world' } + , doc3 = { a: 2, tf: 'bloup' } + ; + + try { + idx.insert([doc1, doc2, doc2b, doc3]); + } catch (e) { + e.errorType.should.equal('uniqueViolated'); + } + idx.tree.getNumberOfKeys().should.equal(0); + assert.deepEqual(idx.tree.search('hello'), []); + assert.deepEqual(idx.tree.search('world'), []); + assert.deepEqual(idx.tree.search('bloup'), []); + }); + }); // ==== End of 'Insertion' ==== //