diff --git a/lib/indexes.js b/lib/indexes.js index cb000b3..13e1a3c 100644 --- a/lib/indexes.js +++ b/lib/indexes.js @@ -62,7 +62,9 @@ Index.prototype.reset = function (newData) { * O(log(n)) */ Index.prototype.insert = function (doc) { - var key, self = this; + var key, self = this + , keys, i, failingI, error + ; if (util.isArray(doc)) { this.insertMultipleDocs(doc); return; } @@ -74,9 +76,26 @@ Index.prototype.insert = function (doc) { if (!util.isArray(key)) { this.tree.insert(key, doc); } else { - _.uniq(key, projectForUnique).forEach(function (_key) { - self.tree.insert(_key, doc); - }); + // If an insert fails due to a unique constraint, roll back all inserts before it + keys = _.uniq(key, projectForUnique); + + for (i = 0; i < keys.length; i += 1) { + try { + this.tree.insert(keys[i], doc); + } catch (e) { + error = e; + failingI = i; + break; + } + } + + if (error) { + for (i = 0; i < failingI; i += 1) { + this.tree.delete(keys[i], doc); + } + + throw error; + } } }; diff --git a/test/indexes.test.js b/test/indexes.test.js index 13399d0..7060c71 100644 --- a/test/indexes.test.js +++ b/test/indexes.test.js @@ -177,7 +177,7 @@ describe('Indexes', function () { (function () { idx.insert(obj2); }).should.throw(); }); - it('When removing a document, remove it from the index at all array elements', function () { + it('When removing a document, remove it from the index at all unique array elements', function () { var obj = { tf: ['aa', 'aa'], really: 'yeah' } , obj2 = { tf: ['cc', 'aa', 'cc'], yes: 'indeed' } , idx = new Index({ fieldName: 'tf' }) @@ -196,6 +196,27 @@ describe('Indexes', function () { idx.getMatching('aa').indexOf(obj2).should.equal(-1); idx.getMatching('cc').length.should.equal(0); }); + + it('If a unique constraint is violated when inserting an array key, roll back all inserts before the key', function () { + var obj = { tf: ['aa', 'bb'], really: 'yeah' } + , obj2 = { tf: ['cc', 'dd', 'aa'], yes: 'indeed' } + , idx = new Index({ fieldName: 'tf', unique: true }) + ; + + idx.insert(obj); + idx.getAll().length.should.equal(2); + idx.getMatching('aa').length.should.equal(1); + idx.getMatching('bb').length.should.equal(1); + idx.getMatching('cc').length.should.equal(0); + idx.getMatching('dd').length.should.equal(0); + + (function () { idx.insert(obj2); }).should.throw(); + idx.getAll().length.should.equal(2); + idx.getMatching('aa').length.should.equal(1); + idx.getMatching('bb').length.should.equal(1); + idx.getMatching('cc').length.should.equal(0); + idx.getMatching('dd').length.should.equal(0); + }); }); // ==== End of 'Array fields' ==== //