diff --git a/lib/model.js b/lib/model.js index b76b5e73ad9..c2786efc4af 100644 --- a/lib/model.js +++ b/lib/model.js @@ -1688,6 +1688,38 @@ Model.findOne = function findOne(conditions, projection, options, callback) { return mq.findOne(conditions, callback); }; +/** + * Counts number of matching documents in a database collection. This method + * is deprecated, use `countDocuments()` instead. + * + * ####Example: + * + * Adventure.count({ type: 'jungle' }, function (err, count) { + * if (err) .. + * console.log('there are %d jungle adventures', count); + * }); + * + * @deprecated + * @param {Object} conditions + * @param {Function} [callback] + * @return {Query} + * @api public + */ + +Model.countDocuments = function count(conditions, callback) { + if (typeof conditions === 'function') { + callback = conditions; + conditions = {}; + } + + // get the mongodb collection object + const mq = new this.Query({}, {}, this, this.collection); + + callback = this.$wrapCallback(callback); + + return mq.countDocuments(conditions, callback); +}; + /** * Counts number of matching documents in a database collection. * diff --git a/lib/query.js b/lib/query.js index 596935902b5..bcb1a6c3651 100644 --- a/lib/query.js +++ b/lib/query.js @@ -1748,7 +1748,33 @@ Query.prototype._count = function(callback) { }; /** - * Specifying this query as a `count` query. + * Thunk around countDocuments() + * + * @param {Function} [callback] + * @see countDocuments http://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#countDocuments + * @api private + */ + +Query.prototype._countDocuments = function(callback) { + try { + this.cast(this.model); + } catch (err) { + this.error(err); + } + + if (this.error()) { + return callback(this.error()); + } + + const conds = this._conditions; + const options = this._optionsForExec(); + + this._collection.collection.countDocuments(conds, options, utils.tick(callback)); +}; + +/** + * Specifies this query as a `count` query. This method is deprecated, use + * `countDocuments()` instead. * * Passing a `callback` executes the query. * @@ -1769,6 +1795,7 @@ Query.prototype._count = function(callback) { * console.log('there are %d kittens', count); * }) * + * @deprecated * @param {Object} [criteria] mongodb selector * @param {Function} [callback] optional params are (error, count) * @return {Query} this @@ -1798,6 +1825,58 @@ Query.prototype.count = function(conditions, callback) { return this; }; +/** + * Specifies this query as a `countDocuments()` query. Behaves like `count()`, + * except for [`$where` and a couple geospatial operators](http://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#countDocuments). + * + * Passing a `callback` executes the query. + * + * This function triggers the following middleware. + * + * - `countDocuments()` + * + * ####Example: + * + * const countQuery = model.where({ 'color': 'black' }).countDocuments(); + * + * query.countDocuments({ color: 'black' }).count(callback); + * + * query.countDocuments({ color: 'black' }, callback); + * + * query.where('color', 'black').countDocuments(function(err, count) { + * if (err) return handleError(err); + * console.log('there are %d kittens', count); + * }); + * + * @param {Object} [criteria] mongodb selector + * @param {Function} [callback] optional params are (error, count) + * @return {Query} this + * @see countDocuments http://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#countDocuments + * @api public + */ + +Query.prototype.countDocuments = function(conditions, callback) { + if (typeof conditions === 'function') { + callback = conditions; + conditions = undefined; + } + + conditions = utils.toObject(conditions); + + if (mquery.canMerge(conditions)) { + this.merge(conditions); + } + + this.op = 'countDocuments'; + if (!callback) { + return this; + } + + this._countDocuments(callback); + + return this; +}; + /** * Declares or executes a distict() operation. * diff --git a/test/model.test.js b/test/model.test.js index e414cf5c0d0..091158d9ca6 100644 --- a/test/model.test.js +++ b/test/model.test.js @@ -3319,6 +3319,16 @@ describe('Model', function() { }); }); + it('countDocuments()', function() { + const BlogPost = db.model('BlogPost' + random(), bpSchema); + + return BlogPost.create({ title: 'foo' }). + then(() => BlogPost.count({ title: 'foo' }).exec()). + then(count => { + assert.equal(count, 1); + }); + }); + it('update()', function(done) { var col = 'BlogPost' + random(); var BlogPost = db.model(col, bpSchema); diff --git a/test/query.test.js b/test/query.test.js index d6d93e07708..48b9a3e3ef8 100644 --- a/test/query.test.js +++ b/test/query.test.js @@ -1129,7 +1129,7 @@ describe('Query', function() { assert.ifError(error); M.deleteOne({ name: /Stark/ }, function(error) { assert.ifError(error); - M.count({}, function(error, count) { + M.countDocuments({}, function(error, count) { assert.ifError(error); assert.equal(count, 1); done(); @@ -1250,7 +1250,7 @@ describe('Query', function() { Test.remove({ name: /Stark/ }).setOptions({ single: true }).exec(function(error, res) { assert.ifError(error); assert.equal(res.n, 1); - Test.count({}, function(error, count) { + Test.countDocuments({}, function(error, count) { assert.ifError(error); assert.equal(count, 1); done(); @@ -1671,6 +1671,12 @@ describe('Query', function() { }); }); + it('ignores sort when passed to countDocuments', function() { + var Product = db.model('Product', 'Product_setOptions_test'); + return Product.create({}). + then(() => Product.find().sort({_id: 1}).countDocuments({}).exec()); + }); + it('ignores count when passed to sort', function(done) { var Product = db.model('Product', 'Product_setOptions_test'); Product.find().count({}).sort({_id: 1}).exec(function(error) { @@ -2698,7 +2704,7 @@ describe('Query', function() { Object.assign(query, { price: priceQuery }); yield Model.create(tests); - var count = yield Model.count(query); + var count = yield Model.countDocuments(query); assert.strictEqual(count, 9); }); });