From 14f4b375ec79629ef9bdcebe41f2afeeb7bbc279 Mon Sep 17 00:00:00 2001 From: Louis Chatriot Date: Sat, 25 Jan 2014 13:41:44 +0100 Subject: [PATCH] Added the sort option, first test done --- lib/cursor.js | 33 +++++++++++++++---- lib/datastore.js | 2 ++ test/cursor.test.js | 78 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+), 6 deletions(-) create mode 100644 test/cursor.test.js diff --git a/lib/cursor.js b/lib/cursor.js index ea3d7da..d03cc8a 100644 --- a/lib/cursor.js +++ b/lib/cursor.js @@ -12,7 +12,7 @@ var model = require('./model'); */ function Cursor (db, query) { this.db = db; - this.query = query; + this.query = query || {}; } @@ -20,7 +20,16 @@ function Cursor (db, query) { * Set a limit the number of results */ Cursor.prototype.limit = function(limit) { - this.limit = limit; + this._limit = limit; +}; + + +/** + * Sort results of the query + * @Param {SortQuery} sortQuery - SortQuery is { field: order }, field can use the dot-notation, order is 1 for ascending and -1 for descending + */ +Cursor.prototype.sort = function(sortQuery) { + this._sort = sortQuery; }; @@ -30,7 +39,7 @@ Cursor.prototype.limit = function(limit) { */ Cursor.prototype.exec = function(callback) { var candidates = this.db.getCandidates(this.query) - , res = [], added = 0 + , res = [], added = 0, self = this , i ; @@ -38,14 +47,26 @@ Cursor.prototype.limit = function(limit) { for (i = 0; i < candidates.length; i += 1) { if (model.match(candidates[i], this.query)) { res.push(model.deepCopy(candidates[i])); - added += 1; - if (this.limit && this.limit <= added) { break; } + + if (!this._sort) { // If a sort is defined, wait for the results to be sorted before applying limit and skip + added += 1; + if (this._limit && this._limit <= added) { break; } + } } } } catch (err) { return callback(err); } - + + // Apply all sorts + if (this._sort) { + Object.keys(this._sort).forEach(function(key) { + res.sort(function(a, b) { + return self._sort[key] * model.compareThings(model.getDotValue(a, key), model.getDotValue(b, key)); + }); + }); + } + return callback(null, res); }; diff --git a/lib/datastore.js b/lib/datastore.js index ac7a745..266344e 100644 --- a/lib/datastore.js +++ b/lib/datastore.js @@ -219,6 +219,8 @@ Datastore.prototype.updateIndexes = function (oldDoc, newDoc) { * We try the following query types, in this order: basic match, $in match, comparison match * One way to make it better would be to enable the use of multiple indexes if the first usable index * returns too much data. I may do it in the future. + * + * TODO: needs to be moved to the Cursor module */ Datastore.prototype.getCandidates = function (query) { var indexNames = Object.keys(this.indexes) diff --git a/test/cursor.test.js b/test/cursor.test.js new file mode 100644 index 0000000..347f43c --- /dev/null +++ b/test/cursor.test.js @@ -0,0 +1,78 @@ +var should = require('chai').should() + , assert = require('chai').assert + , testDb = 'workspace/test.db' + , fs = require('fs') + , path = require('path') + , _ = require('underscore') + , async = require('async') + , model = require('../lib/model') + , Datastore = require('../lib/datastore') + , Persistence = require('../lib/persistence') + , Cursor = require('../lib/cursor') + ; + + +describe.only('Cursor', function () { + var d; + + beforeEach(function (done) { + d = new Datastore({ filename: testDb }); + d.filename.should.equal(testDb); + d.inMemoryOnly.should.equal(false); + + async.waterfall([ + function (cb) { + Persistence.ensureDirectoryExists(path.dirname(testDb), function () { + fs.exists(testDb, function (exists) { + if (exists) { + fs.unlink(testDb, cb); + } else { return cb(); } + }); + }); + } + , function (cb) { + d.loadDatabase(function (err) { + assert.isNull(err); + d.getAllData().length.should.equal(0); + return cb(); + }); + } + ], done); + }); + + describe('Sorting of the results', function () { + + it('Using one sort', function (done) { + var cursor, i; + + d.insert({ age: 5 }); + d.insert({ age: 57 }); + d.insert({ age: 52 }); + d.insert({ age: 23 }); + d.insert({ age: 89 }, function (err) { // We only need the last callback as all operations are queued + cursor = new Cursor(d, {}); + cursor.sort({ age: 1 }); + cursor.exec(function (err, docs) { + assert.isNull(err); + // Results are in ascending order + for (i = 0; i < docs.length - 1; i += 1) { + assert(docs[i].age < docs[i + 1].age) + } + + cursor.sort({ age: -1 }); + cursor.exec(function (err, docs) { + assert.isNull(err); + // Results are in descending order + for (i = 0; i < docs.length - 1; i += 1) { + assert(docs[i].age > docs[i + 1].age) + } + + done(); + }); + }); + }); + }); + + }); + +});