如何在mongoose的同一个字段返回多个计数?

基本上我想把两个单独的调用结合成一个单一的调用,但我不完全确定如何去做。 如果有人能给我一些指导,我将不胜感激! 谢谢!

RatingSchema.statics.getPostRating = function(post, callback) { this.count({ post: post, positiveReview: true }, function(err, posCount){ if(err) { callback(err); return; } this.count({ post: , positiveReview: false }, function(err, negCount){ if(err) { callback(err); return } callback(err, posCount, negCount) } } 

正如已经指出的那样,你可以在一个实际的查询中使用聚合框架来做到这一点,甚至有几种方法来实现这个结果,这取决于你想要的结果。 但实际上有一点关于.count()性能问题作为一个通用的方法,最好用一个例子来演示。

首先,我将在shell中设置一些数据以方便使用:

 var bulk = db.testcol.initializeOrderedBulkOp(); for ( var x=1; x <= 100000; x++ ) { bulk.insert({ value: Math.floor(Math.random(2)*2) }); if ( x % 1000 == 0 ) { bulk.execute(); bulk = db.testcol.initializeOrderedBulkOp(); } } 

因此,仅仅是一个10万个文档集合,只有很less的数据和索引,因为在这种情况下实际上并没有什么不同。 分布应该相当均匀和随机地expression。

然后用一些基本代码来对不同的方法进行抽样

 var async = require('async'), mongoose = require('mongoose'), Schema = mongoose.Schema; var testSchema = new Schema({ value: Number }); mongoose.connect('mongodb://localhost/test'); var Test = mongoose.model( 'Test', testSchema, 'testcol' ); async.series( [ // Time aggregation two results function(callback) { var start = new Date(); Test.aggregate( [{ "$group": { "_id": "$value", "count": { "$sum": 1 } } }], function(err,result) { var obj = { "start": start, "end": new Date() }; obj.time = obj.end.valueOf() - obj.start.valueOf(); obj.result = result; callback(err,obj); } ); }, // Time aggregation conditional function(callback) { var start = new Date(); Test.aggregate( [ { "$group": { "_id": null, "positive": { "$sum": { "$cond": [ { "$eq": [ "$value", 1 ] }, 1, 0 ] } }, "negative": { "$sum": { "$cond": [ { "$eq": [ "$value", 0 ] }, 1, 0 ] } } }} ], function(err,result) { var obj = { "start": start, "end": new Date() }; obj.time = obj.end.valueOf() - obj.start.valueOf(); obj.result = result; callback(err,obj); } ); }, // Time query parallel function(callback) { var start = new Date(); async.parallel( [ function(callback) { Test.count({ value: 1 },callback); }, function(callback) { Test.count({ value: 0 },callback); } ], function(err,results) { var obj = { "start": start, "end": new Date() }; obj.time = obj.end.valueOf() - obj.start.valueOf(); obj.result = results; callback(err,obj); } ); } ], function(err,results) { if (err) throw err; console.log( JSON.stringify( results, undefined, 2 ) ); } ); 

当然,结果是最重要的一点:

 [ { "start": "2014-10-01T08:18:28.059Z", "end": "2014-10-01T08:18:28.263Z", "time": 204, "result": [ { "_id": 1, "count": 49965 }, { "_id": 0, "count": 50035 } ] }, { "start": "2014-10-01T08:18:28.264Z", "end": "2014-10-01T08:18:28.404Z", "time": 140, "result": [ { "_id": null, "positive": 49965, "negative": 50035 } ] }, { "start": "2014-10-01T08:18:28.405Z", "end": "2014-10-01T08:18:28.491Z", "time": 86, "result": [ 49965, 50035 ] } ] 

所以,没有任何进一步的操作,结果显示(并且公平地说,这是经过几次迭代才确定数据被“加热”并加载到内存中),每种forms都有显着的差异。

“第一个”结果是一个基本的聚合语句,它返回包含每个“值”存在计数的两行。 这些只能是10的插入条件,但你可以看到这是204ms的时间。

“第二个”结果是具有聚合的单个文档结果。 这使用$cond操作符来将每个结果“拆分”成一个文档中的属性。 这里所花费的时间在140毫秒时显着减less。

最后,对于“第三个”结果,响应由两个使用“async.parallel”同时执行的查询合并来pipe理结果的并行运行和sorting。 所花费的时间是86ms,小于原始聚合语句的一半,并且仍然显着小于另一个更快的聚合选项。

为什么是这样? 那么,MongoDB本身在执行常规查询时在查询引擎返回的“游标”中保存了一些特定的信息。 部分信息是返回结果的“数量”。 由于查询引擎已经在扫描和积累这个“匹配”的总和这个数字是存在的,没有更多的工作要求,以获得“计数”的工作。

相比之下,汇总框架和$group在许多方面都是有用的,但是却以一种非常不同的方式来实现。 这在两种聚合方法之间的性能差异中部分显而易见,但主要的是基本的“查询引擎”以更高效的方式“计数匹配”事物。

根据实际数据,特别是对于这种true/false匹配,那么对该属性的索引甚至应该产生“更快”的结果。

但是这里的主要观点是,为了简单地“计数”一个属性的匹配值,在这种情况下可以这样做( true/false是一个好例子),那么最高性能的select就是运行“并行查询”已经在这个例子中显示。 性能改进通常是您“计数”的不同属性值数量的一个因素。

所以聚合是伟大的,但在这种情况下,它不是赢家。 mongoose使用的节点本地驱动程序(与许多好的驱动程序实现一样)默认使用“连接池”。 虽然这对于事件驱动的应用程序来说通常是一个好主意,因为有其他并发操作可用的连接,但实际运行多个并发操作以获得结果也是有效的。

通用查询引擎中的优化结合同时发布.count()语句,然后确保您等待合并结果,为您提供此类操作的最佳性能结果。 一般来说,基本计数之外的任何事情都不会是真实的,但这一切都取决于你实际上想要做什么。

部分testing驱动开发通常应该是“testing替代案例”。 这将根据获得的结果引导您朝着正确的方向前进。

你可以使用聚合 ,通过positiveReview分组:

 RatingSchema.statics.getPostRating = function(post, callback) { this.aggregate([ { "$group": { "_id": "$positiveReview", "count": {"$sum": 1} } } ], function(err, results){ // in **results**, you have count by `positiveReview` callback(err, results); }); }