diff --git a/lib/adapter.js b/lib/adapter.js index f6c4a0722..4d7b9a873 100644 --- a/lib/adapter.js +++ b/lib/adapter.js @@ -126,6 +126,15 @@ Adapter.prototype.find = function () { return stubPromise(); }; Adapter.prototype.findMany = function () { return stubPromise(); }; +/** + * Gets the count of resources by IDs or an arbitrary query. + * + * @param {String|Object} model either a string or the underlying model + * @param {Array|Object} [query] either an array of IDs, or a query object + * @return {Promise} + */ +Adapter.prototype.count = function () { return stubPromise(); }; + /** * Sometimes we need to wait for the database connection first. * This is a stub method that should return a promise, and it should diff --git a/lib/adapters/mongodb.js b/lib/adapters/mongodb.js index 94761a32d..f00b6866f 100644 --- a/lib/adapters/mongodb.js +++ b/lib/adapters/mongodb.js @@ -318,10 +318,21 @@ var deepReplaceFalsies = function(query){ * @param projection {Object} * @returns {Promise} */ + adapter.findMany = function(model, query, projection) { + return adapter._findMany(model, query, projection, false); +} + +adapter.count = function(model, query, projection) { + return adapter._findMany(model, query, projection, true); +} + +adapter._findMany = function(model, query, projection, count) { var _this = this, dbQuery = {}; + // console.log("_findMany", query); + model = typeof model == 'string' ? this._models[model] : model; var pk = model.pk || "_id"; @@ -426,28 +437,48 @@ adapter.findMany = function(model, query, projection) { //Take care of deleted resources query = query || {}; if (projection && !projection.includeDeleted) query.deletedAt = {$exists: false}; - var q = model.find(query) - .limit(projection.limit) - .select(projection.select); - if (projection.sort){ - q.sort(projection.sort); + + + if (count) { + + var q = model.count(query) + .exec(function(error, result) { + + if(error) { + return reject(error); + } + + console.log("count result", result); + + resolve(result); + }); } - q.skip(projection.skip) - .exec(function(error, resources) { - if(error) { - return reject(error); - } + else { - resources = resources.map(function (resource) { - var temp = _this._deserialize(model, resource); - if (pkNotRequested){ - //Remove business pk field if it's not required - delete temp[model.pk]; + var q = model.find(query) + .limit(projection.limit) + .select(projection.select); + if (projection.sort){ + q.sort(projection.sort); + } + q.skip(projection.skip) + .exec(function(error, resources) { + if(error) { + return reject(error); } - return temp; + + resources = resources.map(function (resource) { + var temp = _this._deserialize(model, resource); + if (pkNotRequested){ + //Remove business pk field if it's not required + delete temp[model.pk]; + } + return temp; + }); + resolve(resources); }); - resolve(resources); - }); + + } }); }; diff --git a/lib/route.js b/lib/route.js index ebe1347d9..b857d19de 100644 --- a/lib/route.js +++ b/lib/route.js @@ -86,6 +86,7 @@ function route(name, model, resources, inflect, querytree) { } linked.then(function(object) { + var str = production ? JSON.stringify(object, null, null) : JSON.stringify(object, null, 2) + '\n'; @@ -357,6 +358,7 @@ function route(name, model, resources, inflect, querytree) { req.query.include += ',' + inclusions.join(',') : inclusions.join(',') } + sendResponse(req, res, 201, body); } } @@ -407,8 +409,22 @@ function route(name, model, resources, inflect, querytree) { beforeReadHook({}, req, res) .then(function(){ // get resources by IDs + return querytree.parse(model.modelName, _.extend(match, req.query.filter)).then(function(query){ - return adapter.findMany(model, query, projection); + + var queries = []; + queries.push(adapter.findMany(model, query, projection)); + + if(req.query.includeMeta) { + queries.push(adapter.count(model, {}, projection)); + + if (req.query.filter) { + queries.push(adapter.count(model, query, projection)); + } + } + + return RSVP.all(queries); + // return adapter.findMany(model, query, projection); }); }, function(err){ sendError(req, res, 500, err); @@ -416,17 +432,33 @@ function route(name, model, resources, inflect, querytree) { // run after read .then(function(resources) { - return RSVP.all(resources.map(function(resource) { + return RSVP.all(resources[0].map(function(resource) { return afterReadHook(resource, req, res); - })); + })) + .then(function(r) { + var out = { resources : r }; + if (resources.length > 1) out.count = resources[1]; + if (resources.length > 2) out.filterCount = resources[2]; + + return out; + }); }, function(error) { sendError(req, res, 500, error); }) // send the response - .then(function(resources) { + .then(function(r) { + + console.log("r", r); var body = {}; - body[collection] = resources; + body[collection] = r.resources; + + if (r.count) { + body.meta = { count : r.count }; + + if (r.filterCount) body.meta.filterCount = r.filterCount; + } + sendResponse(req, res, 200, body); }, function(error) { sendError(req, res, 403, error); diff --git a/test/fortune-mongodb/mongodb.spec.js b/test/fortune-mongodb/mongodb.spec.js index 189cf3def..c1b0451b6 100644 --- a/test/fortune-mongodb/mongodb.spec.js +++ b/test/fortune-mongodb/mongodb.spec.js @@ -244,6 +244,22 @@ module.exports = function(options){ }); }); describe('Select', function(){ + describe('count @now', function(){ + it('should provide interface for counting resources', function(done){ + var projection = { + select: ['name'] + }; + (function(){ + adapter.count('person', {}, projection) + .then(function(docs){ + console.log(docs); + should.exist(docs); + done(); + }); + }).should.not.throw(); + }); + }); + describe('findMany', function(){ it('should provide interface for selecting fields to return', function(done){ var projection = {