From b0bbf86e6dd8211037f58079c50244a1197c947c Mon Sep 17 00:00:00 2001 From: Antti Saarinen Date: Mon, 29 Jul 2013 23:49:31 +0300 Subject: [PATCH] added Datastore.count with documentation and tests --- README.md | 13 ++++++++ lib/datastore.js | 29 +++++++++++++++++ test/db.test.js | 84 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 126 insertions(+) diff --git a/README.md b/README.md index c853992..37461c4 100644 --- a/README.md +++ b/README.md @@ -164,6 +164,19 @@ db.findOne({ _id: 'id1' }, function (err, doc) { }); ``` +#### Counting objects +Similar to `find`, you can count the number of matching documents by calling `count`. For example + +```javascript +// Using same datastore as above + +// Count all planets in the solar system +db.count({ system: 'solar' }, function (err, count) { + // count equals to 3 + // If no document is found, count is 0 +}); +``` + #### Operators ($lt, $lte, $gt, $gte, $in, $nin, $ne, $exists, $regex) The syntax is `{ field: { $op: value } }` where `$op` is any comparison operator: diff --git a/lib/datastore.js b/lib/datastore.js index f3fecaa..40ae874 100644 --- a/lib/datastore.js +++ b/lib/datastore.js @@ -272,6 +272,35 @@ Datastore.prototype.insert = function () { this.executor.push({ this: this, fn: this._insert, arguments: arguments }); }; +/** + * Count all documents matching the query + * @param {Object} query MongoDB-style query + * + * @api private Use count + */ +Datastore.prototype._count = function(query, callback) { + var res = 0 + , self = this + , candidates = this.getCandidates(query) + , i + ; + + try { + for (i = 0; i < candidates.length; i += 1) { + if (model.match(candidates[i], query)) { + res++; + } + } + } catch (err) { + return callback(err); + } + + return callback(null, res); +} + +Datastore.prototype.count = function() { + this.executor.push({this: this, fn: this._count, arguments: arguments }); +} /** * Find all documents matching the query diff --git a/test/db.test.js b/test/db.test.js index 6c4cc68..3249934 100644 --- a/test/db.test.js +++ b/test/db.test.js @@ -484,6 +484,90 @@ describe('Database', function () { }); // ==== End of 'Find' ==== // + describe('Count', function() { + + it('Count all documents if an empty query is used', function (done) { + async.waterfall([ + function (cb) { + d.insert({ somedata: 'ok' }, function (err) { + d.insert({ somedata: 'another', plus: 'additional data' }, function (err) { + d.insert({ somedata: 'again' }, function (err) { return cb(err); }); + }); + }); + } + , function (cb) { // Test with empty object + d.count({}, function (err, docs) { + assert.isNull(err); + docs.should.equal(3); + return cb(); + }); + } + ], done); + }); + + it('Count all documents matching a basic query', function (done) { + async.waterfall([ + function (cb) { + d.insert({ somedata: 'ok' }, function (err) { + d.insert({ somedata: 'again', plus: 'additional data' }, function (err) { + d.insert({ somedata: 'again' }, function (err) { return cb(err); }); + }); + }); + } + , function (cb) { // Test with query that will return docs + d.count({ somedata: 'again' }, function (err, docs) { + assert.isNull(err); + docs.should.equal(2); + return cb(); + }); + } + , function (cb) { // Test with query that doesn't match anything + d.count({ somedata: 'nope' }, function (err, docs) { + assert.isNull(err); + docs.should.equal(0); + return cb(); + }); + } + ], done); + }); + + it('Array fields match if any element matches', function (done) { + d.insert({ fruits: ['pear', 'apple', 'banana'] }, function (err, doc1) { + d.insert({ fruits: ['coconut', 'orange', 'pear'] }, function (err, doc2) { + d.insert({ fruits: ['banana'] }, function (err, doc3) { + d.count({ fruits: 'pear' }, function (err, docs) { + assert.isNull(err); + docs.should.equal(2); + + d.count({ fruits: 'banana' }, function (err, docs) { + assert.isNull(err); + docs.should.equal(2); + + d.count({ fruits: 'doesntexist' }, function (err, docs) { + assert.isNull(err); + docs.should.equal(0); + + done(); + }); + }); + }); + }); + }); + }); + }); + + it('Returns an error if the query is not well formed', function (done) { + d.insert({ hello: 'world' }, function () { + d.count({ $or: { hello: 'world' } }, function (err, docs) { + assert.isDefined(err); + assert.isUndefined(docs); + + done(); + }); + }); + }); + + }); describe('Update', function () {