diff --git a/lib/datastore.js b/lib/datastore.js index e93830d..2af3da3 100644 --- a/lib/datastore.js +++ b/lib/datastore.js @@ -2,6 +2,7 @@ * The datastore itself * TODO * Queue operations + * Enable upserts * Update and removes should only modify the corresponding part of the database */ @@ -9,6 +10,7 @@ var fs = require('fs') , path = require('path') , customUtils = require('./customUtils') , model = require('./model') + , async = require('async') ; @@ -107,11 +109,6 @@ Datastore.match = function (obj, query) { //, queryKeys = Object.keys(query) , i, k; - //for (i = 0; i < queryKeys.length; i += 1) { - //k = queryKeys[i] - //if (obj[k] !== query[k]) { match = false; } - //} - Object.keys(query).forEach(function (k) { if (obj[k] !== query[k]) { match = false; } }); @@ -206,33 +203,54 @@ Datastore.modify = function (obj, updateQuery) { * @param {Object} newDoc Will replace the former docs * @param {Object} options Optional options * options.multi If true, can update multiple documents (defaults to false) - * @param {Function} cb Optional callback, signature: err, numReplaced + * options.upsert If true, document is inserted if the query doesn't match anything + * @param {Function} cb Optional callback, signature: err, numReplaced, upsert (set to true if the update was in fact an upsert) */ Datastore.prototype.update = function (query, newDoc, options, cb) { var callback , self = this , numReplaced = 0 - , multi + , multi, upsert , newData = []; if (typeof options === 'function') { cb = options; options = {}; } callback = cb || function () {}; multi = options.multi !== undefined ? options.multi : false; + upsert = options.upsert !== undefined ? options.upsert : false; - self.data.forEach(function (d) { - if (Datastore.match(d, query) && (multi || numReplaced === 0)) { - numReplaced += 1; - newData.push(Datastore.modify(d, newDoc)); - } else { - newData.push(d); - } - }); + async.waterfall([ + function (cb) { // If upsert option is set, check whether we need to insert the doc + if (!upsert) { return cb(); } - self.persistWholeDatabase(newData, function (err) { - if (err) { return callback(err); } - self.data = newData; - return callback(null, numReplaced); - }); + self.findOne(query, function (err, doc) { + if (err) { return callback(err); } + if (doc) { + return cb(); + } else { + return self.insert(newDoc, function (err) { + if (err) { return callback(err); } + return callback(null, 1, true); + }); + } + }); + } + , function () { // Perform the update + self.data.forEach(function (d) { + if (Datastore.match(d, query) && (multi || numReplaced === 0)) { + numReplaced += 1; + newData.push(Datastore.modify(d, newDoc)); + } else { + newData.push(d); + } + }); + + self.persistWholeDatabase(newData, function (err) { + if (err) { return callback(err); } + self.data = newData; + return callback(null, numReplaced); + }); + } + ]); }; diff --git a/package.json b/package.json index 4823b6c..04b053e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nedb", - "version": "0.0.3", + "version": "0.0.4", "author": { "name": "tldr.io", "email": "hello@tldr.io" diff --git a/test/db.test.js b/test/db.test.js index 3a2959a..2606998 100644 --- a/test/db.test.js +++ b/test/db.test.js @@ -355,6 +355,31 @@ describe('Database', function () { ], done); }); + it('Can perform upserts if needed', function (done) { + d.update({ impossible: 'db is empty anyway' }, { newDoc: true }, {}, function (err, nr, upsert) { + assert.isNull(err); + nr.should.equal(0); + assert.isUndefined(upsert); + + d.find({}, function (err, docs) { + docs.length.should.equal(0); // Default option for upsert is false + + d.update({ impossible: 'db is empty anyway' }, { newDoc: true }, { upsert: true }, function (err, nr, upsert) { + assert.isNull(err); + nr.should.equal(1); + upsert.should.equal(true); + + d.find({}, function (err, docs) { + docs.length.should.equal(1); // Default option for upsert is false + docs[0].newDoc.should.equal(true); + + done(); + }); + }); + }); + }); + }); + }); // ==== End of 'Update' ==== //