From 39050a45d4eb0acacedf1f4a32bc493a07fb61a0 Mon Sep 17 00:00:00 2001 From: Louis Chatriot Date: Fri, 31 May 2013 19:04:29 +0200 Subject: [PATCH] Can revert updates, simple or batch --- lib/datastore.js | 34 +++++++++++++++++++------- lib/indexes.js | 17 +++++++++++++ test/indexes.test.js | 57 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+), 9 deletions(-) diff --git a/lib/datastore.js b/lib/datastore.js index 83ad0f6..03cd1e3 100644 --- a/lib/datastore.js +++ b/lib/datastore.js @@ -84,8 +84,7 @@ Datastore.prototype.ensureIndex = function (options, cb) { * Add one or several document(s) to all indexes */ Datastore.prototype.addToIndexes = function (doc) { - var self = this - , i, failingIndex, error + var i, failingIndex, error , keys = Object.keys(this.indexes) ; @@ -123,15 +122,32 @@ Datastore.prototype.removeFromIndexes = function (doc) { /** - * Update a document in all indexes - * TODO: enable rollback if an update fails + * Update one or several documents in all indexes + * If one update violates a constraint, all changes are rolled back */ -Datastore.prototype.updateIndexes = function (doc, newDoc) { - var self = this; +Datastore.prototype.updateIndexes = function (oldDoc, newDoc) { + var i, failingIndex, error + , keys = Object.keys(this.indexes) + ; - Object.keys(this.indexes).forEach(function (i) { - self.indexes[i].update(doc, newDoc); - }); + for (i = 0; i < keys.length; i += 1) { + try { + this.indexes[keys[i]].update(oldDoc, newDoc); + } catch (e) { + failingIndex = i; + error = e; + break; + } + } + + // If an error happened, we need to rollback the insert on all other indexes + if (error) { + for (i = 0; i < failingIndex; i += 1) { + this.indexes[keys[i]].remove(doc); + } + + throw error; + } }; diff --git a/lib/indexes.js b/lib/indexes.js index ca5ed29..565bf7a 100644 --- a/lib/indexes.js +++ b/lib/indexes.js @@ -131,6 +131,23 @@ Index.prototype.update = function (oldDoc, newDoc) { }; +/** + * Revert an update + */ +Index.prototype.revertUpdate = function (oldDoc, newDoc) { + var revert = []; + + if (!util.isArray(oldDoc)) { + this.update(newDoc, oldDoc); + } else { + oldDoc.forEach(function (pair) { + revert.push({ oldDoc: pair.newDoc, newDoc: pair.oldDoc }); + }); + this.update(revert); + } +}; + + /** * Update multiple documents in the index * If a constraint is violated, the changes need to be rolled back diff --git a/test/indexes.test.js b/test/indexes.test.js index baa99a1..b8c45d2 100644 --- a/test/indexes.test.js +++ b/test/indexes.test.js @@ -330,6 +330,63 @@ describe('Indexes', function () { assert.deepEqual(idx.tree.search('world'), [noChange]); }); + it('Can revert normal and batch updates', function () { + var idx = new Index({ fieldName: 'tf' }) + , doc1 = { a: 5, tf: 'hello' } + , doc2 = { a: 8, tf: 'world' } + , doc3 = { a: 2, tf: 'bloup' } + , doc1b = { a: 23, tf: 'world' } + , doc2b = { a: 1, tf: 'changed' } + , doc3b = { a: 44, tf: 'bloup' } + , batchUpdate = [{ oldDoc: doc1, newDoc: doc1b }, { oldDoc: doc2, newDoc: doc2b }, { oldDoc: doc3, newDoc: doc3b }] + ; + + idx.insert(doc1); + idx.insert(doc2); + idx.insert(doc3); + idx.tree.getNumberOfKeys().should.equal(3); + + idx.update(batchUpdate); + + idx.tree.getNumberOfKeys().should.equal(3); + idx.getMatching('world').length.should.equal(1); + idx.getMatching('world')[0].should.equal(doc1b); + idx.getMatching('changed').length.should.equal(1); + idx.getMatching('changed')[0].should.equal(doc2b); + idx.getMatching('bloup').length.should.equal(1); + idx.getMatching('bloup')[0].should.equal(doc3b); + + idx.revertUpdate(batchUpdate); + + idx.tree.getNumberOfKeys().should.equal(3); + idx.getMatching('hello').length.should.equal(1); + idx.getMatching('hello')[0].should.equal(doc1); + idx.getMatching('world').length.should.equal(1); + idx.getMatching('world')[0].should.equal(doc2); + idx.getMatching('bloup').length.should.equal(1); + idx.getMatching('bloup')[0].should.equal(doc3); + + // Now a simple update + idx.update(doc2, doc2b); + + idx.tree.getNumberOfKeys().should.equal(3); + idx.getMatching('hello').length.should.equal(1); + idx.getMatching('hello')[0].should.equal(doc1); + idx.getMatching('changed').length.should.equal(1); + idx.getMatching('changed')[0].should.equal(doc2b); + idx.getMatching('bloup').length.should.equal(1); + idx.getMatching('bloup')[0].should.equal(doc3); + + idx.revertUpdate(doc2, doc2b); + + idx.tree.getNumberOfKeys().should.equal(3); + idx.getMatching('hello').length.should.equal(1); + idx.getMatching('hello')[0].should.equal(doc1); + idx.getMatching('world').length.should.equal(1); + idx.getMatching('world')[0].should.equal(doc2); + idx.getMatching('bloup').length.should.equal(1); + idx.getMatching('bloup')[0].should.equal(doc3); + }); }); // ==== End of 'Update' ==== //