|
|
@ -12,20 +12,38 @@ var fs = require('fs') |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Create a new collection |
|
|
|
* Create a new collection |
|
|
|
* If a filename is provided, persist it to disk |
|
|
|
* @param {String} options.filename Optional, datastore will be in-memory only if not provided |
|
|
|
* Otherwise keep it in memory (data will be lost when the application stops) |
|
|
|
* @param {Boolean} options.inMemoryOnly Optional, default to false |
|
|
|
|
|
|
|
* @param {Boolean} options.pipeline Optional, defaults to false, pipeline appends to the data file (enable to return before writes are persisted to disk for greater speed) |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
function Datastore (filename) { |
|
|
|
function Datastore (options) { |
|
|
|
|
|
|
|
var filename; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Retrocompatibility with v0.6 and before
|
|
|
|
|
|
|
|
if (typeof options === 'string') { |
|
|
|
|
|
|
|
filename = options; |
|
|
|
|
|
|
|
this.inMemoryOnly = false; // Default
|
|
|
|
|
|
|
|
this.pipeline = false; // Default
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
options = options || {}; |
|
|
|
|
|
|
|
filename = options.filename |
|
|
|
|
|
|
|
this.inMemoryOnly = options.inMemoryOnly || false; |
|
|
|
|
|
|
|
this.pipeline = options.pipeline || false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (!filename || typeof filename !== 'string' || filename.length === 0) { |
|
|
|
if (!filename || typeof filename !== 'string' || filename.length === 0) { |
|
|
|
this.filename = null; |
|
|
|
this.filename = null; |
|
|
|
|
|
|
|
this.inMemoryOnly = true; |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
this.filename = filename; |
|
|
|
this.filename = filename; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
this.executor = new Executor(); |
|
|
|
this.executor = new Executor(); |
|
|
|
|
|
|
|
if (this.pipeline) { this.persistenceExecutor = new Executor(); } |
|
|
|
|
|
|
|
|
|
|
|
// We keep internally the number of lines in the datafile
|
|
|
|
// We keep internally the number of lines in the datafile
|
|
|
|
// This will be used when/if I implement autocompacting when the datafile grows too big
|
|
|
|
// This will be used when/if I implement autocompacting when the datafile grows too big
|
|
|
|
|
|
|
|
// For now it is not urgent as autocompaction happens upon every restart
|
|
|
|
this.datafileSize = 0; |
|
|
|
this.datafileSize = 0; |
|
|
|
|
|
|
|
|
|
|
|
// Indexed by field name, dot notation can be used
|
|
|
|
// Indexed by field name, dot notation can be used
|
|
|
@ -202,17 +220,15 @@ Datastore.prototype._loadDatabase = function (cb) { |
|
|
|
, self = this |
|
|
|
, self = this |
|
|
|
; |
|
|
|
; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.resetIndexes(); |
|
|
|
|
|
|
|
self.datafileSize = 0; |
|
|
|
|
|
|
|
|
|
|
|
// In-memory only datastore
|
|
|
|
// In-memory only datastore
|
|
|
|
if (!self.filename) { return callback(); } |
|
|
|
if (self.inMemoryOnly) { return callback(); } |
|
|
|
|
|
|
|
|
|
|
|
customUtils.ensureDirectoryExists(path.dirname(self.filename), function (err) { |
|
|
|
customUtils.ensureDirectoryExists(path.dirname(self.filename), function (err) { |
|
|
|
fs.exists(self.filename, function (exists) { |
|
|
|
fs.exists(self.filename, function (exists) { |
|
|
|
if (!exists) { |
|
|
|
if (!exists) { return fs.writeFile(self.filename, '', 'utf8', function (err) { callback(err); }); } |
|
|
|
self.resetIndexes(); |
|
|
|
|
|
|
|
self.datafileSize = 0; |
|
|
|
|
|
|
|
fs.writeFile(self.filename, '', 'utf8', function (err) { return callback(err); }); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fs.readFile(self.filename, 'utf8', function (err, rawData) { |
|
|
|
fs.readFile(self.filename, 'utf8', function (err, rawData) { |
|
|
|
if (err) { return callback(err); } |
|
|
|
if (err) { return callback(err); } |
|
|
@ -299,14 +315,14 @@ Datastore.prototype.persistCachedDatabase = function (cb) { |
|
|
|
* @param {Array} newDocs Can be empty if no doc was updated/removed |
|
|
|
* @param {Array} newDocs Can be empty if no doc was updated/removed |
|
|
|
* @param {Function} cb Optional, signature: err |
|
|
|
* @param {Function} cb Optional, signature: err |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
Datastore.prototype.persistNewState = function (newDocs, cb) { |
|
|
|
Datastore.prototype._persistNewState = function (newDocs, cb) { |
|
|
|
var self = this |
|
|
|
var self = this |
|
|
|
, toPersist = '' |
|
|
|
, toPersist = '' |
|
|
|
, callback = cb || function () {} |
|
|
|
, callback = cb || function () {} |
|
|
|
; |
|
|
|
; |
|
|
|
|
|
|
|
|
|
|
|
// In-memory only datastore
|
|
|
|
// In-memory only datastore
|
|
|
|
if (!self.filename) { return callback(); } |
|
|
|
if (self.inMemoryOnly) { return callback(); } |
|
|
|
|
|
|
|
|
|
|
|
self.datafileSize += newDocs.length; |
|
|
|
self.datafileSize += newDocs.length; |
|
|
|
|
|
|
|
|
|
|
@ -320,6 +336,9 @@ Datastore.prototype.persistNewState = function (newDocs, cb) { |
|
|
|
return callback(err); |
|
|
|
return callback(err); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
Datastore.prototype.persistNewState = function (newDocs, cb) { |
|
|
|
|
|
|
|
this._persistNewState(newDocs, cb); |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|