CCan specify custom string comparison function

pull/2/head
Louis Chatriot 9 years ago
parent 1ba2f3abf6
commit cd80806d24
  1. 2
      lib/cursor.js
  2. 4
      lib/datastore.js
  3. 11
      lib/model.js
  4. 91
      test/cursor.test.js
  5. 5
      test/model.test.js

@ -145,7 +145,7 @@ Cursor.prototype._exec = function(callback) {
var criterion, compare, i; var criterion, compare, i;
for (i = 0; i < criteria.length; i++) { for (i = 0; i < criteria.length; i++) {
criterion = criteria[i]; criterion = criteria[i];
compare = criterion.direction * model.compareThings(model.getDotValue(a, criterion.key), model.getDotValue(b, criterion.key)); compare = criterion.direction * model.compareThings(model.getDotValue(a, criterion.key), model.getDotValue(b, criterion.key), self.db.compareStrings);
if (compare !== 0) { if (compare !== 0) {
return compare; return compare;
} }

@ -21,6 +21,7 @@ var customUtils = require('./customUtils')
* @param {Function} options.onload Optional, if autoload is used this will be called after the load database with the error object as parameter. If you don't pass it the error will be thrown * @param {Function} options.onload Optional, if autoload is used this will be called after the load database with the error object as parameter. If you don't pass it the error will be thrown
* @param {Function} options.afterSerialization/options.beforeDeserialization Optional, serialization hooks * @param {Function} options.afterSerialization/options.beforeDeserialization Optional, serialization hooks
* @param {Number} options.corruptAlertThreshold Optional, threshold after which an alert is thrown if too much data is corrupt * @param {Number} options.corruptAlertThreshold Optional, threshold after which an alert is thrown if too much data is corrupt
* @param {Function} options.compareStrings Optional, string comparison function that overrides default for sorting
*/ */
function Datastore (options) { function Datastore (options) {
var filename; var filename;
@ -45,6 +46,9 @@ function Datastore (options) {
this.filename = filename; this.filename = filename;
} }
// String comparison function
this.compareStrings = options.compareStrings;
// Persistence handling // Persistence handling
this.persistence = new Persistence({ db: this, nodeWebkitAppName: options.nodeWebkitAppName this.persistence = new Persistence({ db: this, nodeWebkitAppName: options.nodeWebkitAppName
, afterSerialization: options.afterSerialization , afterSerialization: options.afterSerialization

@ -183,9 +183,12 @@ function compareArrays (a, b) {
* In the case of objects and arrays, we deep-compare * In the case of objects and arrays, we deep-compare
* If two objects dont have the same type, the (arbitrary) type hierarchy is: undefined, null, number, strings, boolean, dates, arrays, objects * If two objects dont have the same type, the (arbitrary) type hierarchy is: undefined, null, number, strings, boolean, dates, arrays, objects
* Return -1 if a < b, 1 if a > b and 0 if a = b (note that equality here is NOT the same as defined in areThingsEqual!) * Return -1 if a < b, 1 if a > b and 0 if a = b (note that equality here is NOT the same as defined in areThingsEqual!)
*
* @param {Function} _compareStrings String comparing function, returning -1, 0 or 1, overriding default string comparison (useful for languages with accented letters)
*/ */
function compareThings (a, b) { function compareThings (a, b, _compareStrings) {
var aKeys, bKeys, comp, i; var aKeys, bKeys, comp, i
, compareStrings = _compareStrings || compareNSB;
// undefined // undefined
if (a === undefined) { return b === undefined ? 0 : -1; } if (a === undefined) { return b === undefined ? 0 : -1; }
@ -200,8 +203,8 @@ function compareThings (a, b) {
if (typeof b === 'number') { return typeof a === 'number' ? compareNSB(a, b) : 1; } if (typeof b === 'number') { return typeof a === 'number' ? compareNSB(a, b) : 1; }
// Strings // Strings
if (typeof a === 'string') { return typeof b === 'string' ? compareNSB(a, b) : -1; } if (typeof a === 'string') { return typeof b === 'string' ? compareStrings(a, b) : -1; }
if (typeof b === 'string') { return typeof a === 'string' ? compareNSB(a, b) : 1; } if (typeof b === 'string') { return typeof a === 'string' ? compareStrings(a, b) : 1; }
// Booleans // Booleans
if (typeof a === 'boolean') { return typeof b === 'boolean' ? compareNSB(a, b) : -1; } if (typeof a === 'boolean') { return typeof b === 'boolean' ? compareNSB(a, b) : -1; }

@ -177,7 +177,7 @@ describe('Cursor', function () {
for (i = 0; i < docs.length - 1; i += 1) { for (i = 0; i < docs.length - 1; i += 1) {
assert(docs[i].age < docs[i + 1].age) assert(docs[i].age < docs[i + 1].age)
} }
cursor.sort({ age: -1 }); cursor.sort({ age: -1 });
cursor.exec(function (err, docs) { cursor.exec(function (err, docs) {
assert.isNull(err); assert.isNull(err);
@ -187,29 +187,54 @@ describe('Cursor', function () {
} }
done(); done();
}); });
}); });
}); });
it("Sorting strings with custom string comparison function", function (done) {
var db = new Datastore({ inMemoryOnly: true, autoload: true
, compareStrings: function (a, b) { return a.length - b.length; }
});
db.insert({ name: 'alpha' });
db.insert({ name: 'charlie' });
db.insert({ name: 'zulu' });
db.find({}).sort({ name: 1 }).exec(function (err, docs) {
_.pluck(docs, 'name')[0].should.equal('zulu');
_.pluck(docs, 'name')[1].should.equal('alpha');
_.pluck(docs, 'name')[2].should.equal('charlie');
delete db.compareStrings;
db.find({}).sort({ name: 1 }).exec(function (err, docs) {
_.pluck(docs, 'name')[0].should.equal('alpha');
_.pluck(docs, 'name')[1].should.equal('charlie');
_.pluck(docs, 'name')[2].should.equal('zulu');
done();
});
});
});
it('With an empty collection', function (done) { it('With an empty collection', function (done) {
async.waterfall([ async.waterfall([
function (cb) { function (cb) {
d.remove({}, { multi: true }, function(err) { return cb(err); }) d.remove({}, { multi: true }, function(err) { return cb(err); })
} }
, function (cb) { , function (cb) {
var cursor = new Cursor(d); var cursor = new Cursor(d);
cursor.sort({ age: 1 }); cursor.sort({ age: 1 });
cursor.exec(function (err, docs) { cursor.exec(function (err, docs) {
assert.isNull(err); assert.isNull(err);
docs.length.should.equal(0); docs.length.should.equal(0);
cb(); cb();
}); });
} }
], done); ], done);
}); });
it('Ability to chain sorting and exec', function (done) { it('Ability to chain sorting and exec', function (done) {
var i; var i;
async.waterfall([ async.waterfall([
function (cb) { function (cb) {
var cursor = new Cursor(d); var cursor = new Cursor(d);
@ -223,21 +248,21 @@ describe('Cursor', function () {
}); });
} }
, function (cb) { , function (cb) {
var cursor = new Cursor(d); var cursor = new Cursor(d);
cursor.sort({ age: -1 }).exec(function (err, docs) { cursor.sort({ age: -1 }).exec(function (err, docs) {
assert.isNull(err); assert.isNull(err);
// Results are in descending order // Results are in descending order
for (i = 0; i < docs.length - 1; i += 1) { for (i = 0; i < docs.length - 1; i += 1) {
assert(docs[i].age > docs[i + 1].age) assert(docs[i].age > docs[i + 1].age)
} }
cb(); cb();
}); });
} }
], done); ], done);
}); });
it('Using limit and sort', function (done) { it('Using limit and sort', function (done) {
var i; var i;
async.waterfall([ async.waterfall([
function (cb) { function (cb) {
var cursor = new Cursor(d); var cursor = new Cursor(d);
@ -251,15 +276,15 @@ describe('Cursor', function () {
}); });
} }
, function (cb) { , function (cb) {
var cursor = new Cursor(d); var cursor = new Cursor(d);
cursor.sort({ age: -1 }).limit(2).exec(function (err, docs) { cursor.sort({ age: -1 }).limit(2).exec(function (err, docs) {
assert.isNull(err); assert.isNull(err);
docs.length.should.equal(2); docs.length.should.equal(2);
docs[0].age.should.equal(89); docs[0].age.should.equal(89);
docs[1].age.should.equal(57); docs[1].age.should.equal(57);
cb(); cb();
}); });
} }
], done); ], done);
}); });

@ -813,6 +813,11 @@ describe('Model', function () {
model.compareThings({ a: 42, b: 312, c: 54 }, { b: 313, a: 42 }).should.equal(-1); model.compareThings({ a: 42, b: 312, c: 54 }, { b: 313, a: 42 }).should.equal(-1);
}); });
it('Can specify custom string comparison function', function () {
model.compareThings('hello', 'bloup', function (a, b) { return a < b ? -1 : 1; }).should.equal(1);
model.compareThings('hello', 'bloup', function (a, b) { return a > b ? -1 : 1; }).should.equal(-1);
});
}); // ==== End of 'Comparing things' ==== // }); // ==== End of 'Comparing things' ==== //

Loading…
Cancel
Save