diff --git a/lib/datastore.js b/lib/datastore.js index 512f4f1..e47d1b1 100644 --- a/lib/datastore.js +++ b/lib/datastore.js @@ -247,28 +247,64 @@ Datastore.prototype.getCandidates = function (query) { */ Datastore.prototype._insert = function (newDoc, cb) { var callback = cb || function () {} - , self = this - , insertedDoc ; - // Ensure the document has the right format try { - newDoc._id = customUtils.uid(16); - model.checkObject(newDoc); - insertedDoc = model.deepCopy(newDoc); + this._insertInCache(newDoc); } catch (e) { return callback(e); } - // Insert in all indexes (also serves to ensure uniqueness) - try { self.addToIndexes(insertedDoc); } catch (e) { return callback(e); } - - this.persistence.persistNewState([newDoc], function (err) { + this.persistence.persistNewState(util.isArray(newDoc) ? newDoc : [newDoc], function (err) { if (err) { return callback(err); } return callback(null, newDoc); }); }; +/** + * If newDoc is an array of documents, this will insert all documents in the cache + * @api private + */ +Datastore.prototype._insertInCache = function (newDoc) { + var insertedDoc; + + if (util.isArray(newDoc)) { this._insertMultipleDocsInCache(newDoc); return; } + + // Ensure the document has the right format + newDoc._id = customUtils.uid(16); + model.checkObject(newDoc); + insertedDoc = model.deepCopy(newDoc); + + // Insert in all indexes (also serves to ensure uniqueness) + this.addToIndexes(insertedDoc); +}; + +/** + * If one insertion fails (e.g. because of a unique constraint), roll back all previous + * inserts and throws the error + */ +Datastore.prototype._insertMultipleDocsInCache = function (newDocs) { + var i, failingI, error; + + for (i = 0; i < newDocs.length; i += 1) { + try { + this._insertInCache(newDocs[i]); + } catch (e) { + error = e; + failingI = i; + break; + } + } + + if (error) { + for (i = 0; i < failingI; i += 1) { + this._removeFromCache(newDocs[i]); + } + + throw error; + } +}; + Datastore.prototype.insert = function () { this.executor.push({ this: this, fn: this._insert, arguments: arguments }); }; diff --git a/test/db.test.js b/test/db.test.js index 7a1ff85..18a953c 100644 --- a/test/db.test.js +++ b/test/db.test.js @@ -179,6 +179,30 @@ describe('Database', function () { }); }); }); + + it.only('Can insert an array of documents at once', function (done) { + var docs = [{ a: 5, b: 'hello' }, { a: 42, b: 'world' }]; + + d.insert(docs, function (err) { + d.find({}, function (err, docs) { + var data; + + docs.length.should.equal(2); + _.find(docs, function (doc) { return doc.a === 5; }).b.should.equal('hello'); + _.find(docs, function (doc) { return doc.a === 42; }).b.should.equal('world'); + + // The data has been persisted correctly + data = _.filter(fs.readFileSync(testDb, 'utf8').split('\n'), function (line) { return line.length > 0; }); + data.length.should.equal(2); + model.deserialize(data[0]).a.should.equal(5); + model.deserialize(data[0]).b.should.equal('hello'); + model.deserialize(data[1]).a.should.equal(42); + model.deserialize(data[1]).b.should.equal('world'); + + done(); + }); + }); + }); }); // ==== End of 'Insert' ==== //