|
|
|
@ -15,7 +15,6 @@ var fs = require('fs') |
|
|
|
|
*/ |
|
|
|
|
function Datastore (filename) { |
|
|
|
|
this.filename = filename; |
|
|
|
|
this.data = []; |
|
|
|
|
this.executor = new Executor(); |
|
|
|
|
|
|
|
|
|
// We keep internally the number of lines in the datafile
|
|
|
|
@ -26,10 +25,18 @@ function Datastore (filename) { |
|
|
|
|
// _id is always indexed and since _ids are generated randomly the underlying
|
|
|
|
|
// binary is always well-balanced
|
|
|
|
|
this.indexes = {}; |
|
|
|
|
this.ensureIndex({ fieldName: '_id', unique: true }); |
|
|
|
|
this.indexes._id = new Index({ fieldName: '_id', unique: true }); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Get an array of all the data in the database |
|
|
|
|
*/ |
|
|
|
|
Datastore.prototype.getAllData = function () { |
|
|
|
|
return this.indexes._id.getAll(); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Reset all currently defined indexes |
|
|
|
|
*/ |
|
|
|
@ -63,7 +70,7 @@ Datastore.prototype.ensureIndex = function (options, cb) { |
|
|
|
|
this.indexes[options.fieldName] = new Index(options); |
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
this.indexes[options.fieldName].insert(this.data); |
|
|
|
|
this.indexes[options.fieldName].insert(this.getAllData()); |
|
|
|
|
} catch (e) { |
|
|
|
|
delete this.indexes[options.fieldName]; |
|
|
|
|
return callback(e); |
|
|
|
@ -138,7 +145,7 @@ Datastore.prototype.getCandidates = function (query) { |
|
|
|
|
var indexNames = Object.keys(this.indexes) |
|
|
|
|
, usableQueryKeys; |
|
|
|
|
|
|
|
|
|
if (indexNames.length === 0) { return this.data; } // No index defined, no specific candidate
|
|
|
|
|
if (indexNames.length <= 1) { return this.getAllData(); } // No index defined (except _id), no specific candidate
|
|
|
|
|
|
|
|
|
|
// Usable query keys are the ones corresponding to a basic query (no use of $operators or arrays)
|
|
|
|
|
usableQueryKeys = []; |
|
|
|
@ -153,7 +160,7 @@ Datastore.prototype.getCandidates = function (query) { |
|
|
|
|
if (usableQueryKeys.length > 0) { |
|
|
|
|
return this.indexes[usableQueryKeys[0]].getMatching(query[usableQueryKeys[0]]); |
|
|
|
|
} else { |
|
|
|
|
return this.data; |
|
|
|
|
return this.getAllData(); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
@ -175,7 +182,6 @@ Datastore.prototype._loadDatabase = function (cb) { |
|
|
|
|
customUtils.ensureDirectoryExists(path.dirname(self.filename), function (err) { |
|
|
|
|
fs.exists(self.filename, function (exists) { |
|
|
|
|
if (!exists) { |
|
|
|
|
self.data = []; |
|
|
|
|
self.datafileSize = 0; |
|
|
|
|
fs.writeFile(self.filename, '', 'utf8', function (err) { return callback(err); }); |
|
|
|
|
return; |
|
|
|
@ -183,18 +189,17 @@ Datastore.prototype._loadDatabase = function (cb) { |
|
|
|
|
|
|
|
|
|
fs.readFile(self.filename, 'utf8', function (err, rawData) { |
|
|
|
|
if (err) { return callback(err); } |
|
|
|
|
self.data = Datastore.treatRawData(rawData); |
|
|
|
|
var treatedData = Datastore.treatRawData(rawData); |
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
self.resetIndexes(self.data); |
|
|
|
|
self.resetIndexes(treatedData); |
|
|
|
|
} catch (e) { |
|
|
|
|
self.resetIndexes(); // Rollback any index which didn't fail
|
|
|
|
|
self.datafileSize = 0; |
|
|
|
|
self.data = []; |
|
|
|
|
return callback(e); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
self.datafileSize = self.data.length; |
|
|
|
|
self.datafileSize = treatedData.length; |
|
|
|
|
self.persistCachedDatabase(callback); |
|
|
|
|
}); |
|
|
|
|
}); |
|
|
|
@ -251,7 +256,7 @@ Datastore.prototype.persistCachedDatabase = function (cb) { |
|
|
|
|
, toPersist = '' |
|
|
|
|
; |
|
|
|
|
|
|
|
|
|
this.data.forEach(function (doc) { |
|
|
|
|
this.getAllData().forEach(function (doc) { |
|
|
|
|
toPersist += model.serialize(doc) + '\n'; |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
@ -291,9 +296,8 @@ Datastore.prototype._insert = function (newDoc, cb) { |
|
|
|
|
fs.appendFile(self.filename, persistableNewDoc + '\n', 'utf8', function (err) { |
|
|
|
|
if (err) { return callback(err); } |
|
|
|
|
|
|
|
|
|
self.data.push(insertedDoc); |
|
|
|
|
self.datafileSize += 1; |
|
|
|
|
return callback(null, model.deepCopy(insertedDoc)); |
|
|
|
|
return callback(null, model.deepCopy(insertedDoc)); // CHANGE
|
|
|
|
|
}); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
@ -466,10 +470,8 @@ Datastore.prototype.update = function () { |
|
|
|
|
Datastore.prototype._remove = function (query, options, cb) { |
|
|
|
|
var callback |
|
|
|
|
, self = this |
|
|
|
|
//, candidates = this.getCandidates(query)
|
|
|
|
|
, numRemoved = 0 |
|
|
|
|
, multi |
|
|
|
|
, newData = [] |
|
|
|
|
, removedDocs = [] |
|
|
|
|
; |
|
|
|
|
|
|
|
|
@ -477,23 +479,22 @@ Datastore.prototype._remove = function (query, options, cb) { |
|
|
|
|
callback = cb || function () {}; |
|
|
|
|
multi = options.multi !== undefined ? options.multi : false; |
|
|
|
|
|
|
|
|
|
// CHANGE
|
|
|
|
|
try { |
|
|
|
|
self.data.forEach(function (d) { |
|
|
|
|
self.getAllData().forEach(function (d) { // CHANGE
|
|
|
|
|
if (model.match(d, query) && (multi || numRemoved === 0)) { |
|
|
|
|
numRemoved += 1; |
|
|
|
|
removedDocs.push({ $$deleted: true, _id: d._id }); |
|
|
|
|
self.removeFromIndexes(d); |
|
|
|
|
} else { |
|
|
|
|
newData.push(d); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} catch (err) { |
|
|
|
|
return callback(err); |
|
|
|
|
} |
|
|
|
|
} catch (err) { return callback(err); } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.persistNewState(removedDocs, function (err) { |
|
|
|
|
if (err) { return callback(err); } |
|
|
|
|
self.data = newData; |
|
|
|
|
|
|
|
|
|
return callback(null, numRemoved); |
|
|
|
|
}); |
|
|
|
|
}; |
|
|
|
|