通过协会的mongoose限制

我有这样一个集合:

[ { parent: 'a', d1: '1', d2: '2', d3: '3', w: 10 }, { parent: 'a', d1: '1', d2: '2', d3: '3', w: 20 }, { parent: 'a', d1: '1', d2: '2', d3: '3', w: 30 }, { parent: 'a', d1: '1', d2: '2', d3: '3', w: 40 }, { parent: 'a', d1: '1', d2: '2', d3: '3', w: 50 }, { parent: 'a', d1: '1', d2: '2', d3: '3', w: 60 }, { parent: 'b', d1: '1', d2: '2', d3: '3', w: 10 }, { parent: 'b', d1: '1', d2: '2', d3: '3', w: 13 }, { parent: 'b', d1: '1', d2: '2', d3: '3', w: 14 }, { parent: 'b', d1: '1', d2: '2', d3: '3', w: 15 }, { parent: 'c', d1: '1', d2: '2', d3: '3', w: 10 }, { parent: 'c', d1: '1', d2: '2', d3: '3', w: 100 }, { parent: 'c', d1: '1', d2: '2', d3: '3', w: 200 }, { parent: 'c', d1: '1', d2: '2', d3: '3', w: 300 } ] 

给定一个查询与相关的父母ids, ['b','c'] ,我需要得到每个父母的前3个结果,希望DESC – 按w

 [ { parent: 'b', d1: '1', d2: '2', d3: '3', w: 15 }, { parent: 'b', d1: '1', d2: '2', d3: '3', w: 14 }, { parent: 'b', d1: '1', d2: '2', d3: '3', w: 13 }, { parent: 'c', d1: '1', d2: '2', d3: '3', w: 300 }, { parent: 'c', d1: '1', d2: '2', d3: '3', w: 200 }, { parent: 'c', d1: '1', d2: '2', d3: '3', w: 100 } ] 

使用.find().limit()将返回整个前N个结果,而不是每个parent的前N个。 使用.aggregate()我想通过parent如何聚合,但我不知道如何$limit的父母,也不知道如何返回整个文档为{parent: 'b', items: [{..}, {..}] }而不仅仅是组数据。 我可以得到parent ,我已经有,也可能是parent和数组在某个领域使用$push ,但是这仍然是不好的。

最后我也尝试过.mapReduce但这似乎是矫枉过正,不是我必须emit(this.project, this); 为聚合部分? 我怎么会甚至限制呢? 用手? 这是相当的没有logging。

无论如何,在这条路上的某个方向将会很棒。 我使用mongoose@latest

正如所指出的那样,不幸的是,使用当前存在的MongoDB的聚合框架是无法实现的,正如你所提到的, map-reduce会是一个矫枉过正的事情。

但是还有其他的方法:

方法一:

  • 维护一个表示基于w字段的层次结构的variables,或者要对结果集进行sorting的字段。 一旦在插入过程中将variables添加到每个文档。
  • 您的文档将包含一个名为level的新字段,其中包含一个单个值的数组。 我们将讨论为什么这需要一个数组而不是一个简单的领域。

插入脚本:

 db.collection.insert([ { parent: 'a', d1: '1', d2: '2', d3: '3', w: 10,level:[6] }, { parent: 'a', d1: '1', d2: '2', d3: '3', w: 20,level:[5] }, { parent: 'a', d1: '1', d2: '2', d3: '3', w: 30,level:[4] }, { parent: 'a', d1: '1', d2: '2', d3: '3', w: 40,level:[3] }, { parent: 'a', d1: '1', d2: '2', d3: '3', w: 50,level:[2] }, { parent: 'a', d1: '1', d2: '2', d3: '3', w: 60,level:[1] }, { parent: 'b', d1: '1', d2: '2', d3: '3', w: 10,level:[4] }, { parent: 'b', d1: '1', d2: '2', d3: '3', w: 13,level:[3] }, { parent: 'b', d1: '1', d2: '2', d3: '3', w: 14,level:[2] }, { parent: 'b', d1: '1', d2: '2', d3: '3', w: 15,level:[1] }, { parent: 'c', d1: '1', d2: '2', d3: '3', w: 10,level:[4] }, { parent: 'c', d1: '1', d2: '2', d3: '3', w: 100,level:[3] }, { parent: 'c', d1: '1', d2: '2', d3: '3', w: 200,level:[2] }, { parent: 'c', d1: '1', d2: '2', d3: '3', w: 300,level:[1] } ]) 

假设您想要根据每个父代的w字段的sorting顺序获取排名前3结果。 你可以很容易地聚合如下:

 var levels = [1,2,3]; // indicating the records in the range that we need to pick up, // from each parent. 
  • 匹配所有ab的父母。
  • w字段sortinglogging。
  • parent分组。 一旦你分组,父母的所有文件成为分组logging的子文件,因此允许你应用$ redact阶段。
  • 现在应用$redact阶段来修改那些子级文档,其级别不是我们所寻求的级别的子集。 我们保持level为一个数组,因为它更容易在其上应用$setIsSubset运算符。 否则我们需要$in ,这在$condexpression式中是不被支持的。

码:

 Model.aggregate( {$match:{"parent":{$in:["a","b"]}}}, {$sort:{"w":-1}}, {$group:{"_id":"$parent", "rec":{$push:"$$ROOT"}}}, {$redact:{$cond:[{$setIsSubset:[{$ifNull:["$levels",[1]]}, inp]}, "$$DESCEND","$$PRUNE"]}}, ,function(err,resp){ // handle response }) 

获得的输出是完美的,正如我们想要的:(只显示b组,缩短它)

 { "_id" : "b", "rec" : [ { "_id" : ObjectId("54b030a3e4eae97f395e5e89"), "parent" : "b", "d1" : "1", "d2" : "2", "d3" : "3", "w" : 15, "level" : [ 1 ] }, { "_id" : ObjectId("54b030a3e4eae97f395e5e88"), "parent" : "b", "d1" : "1", "d2" : "2", "d3" : "3", "w" : 14, "level" : [ 2 ] }, { "_id" : ObjectId("54b030a3e4eae97f395e5e87"), "parent" : "b", "d1" : "1", "d2" : "2", "d3" : "3", "w" : 13, "level" : [ 3 ] } ] } 

方法B:

子文档的编写是在客户端完成的:

 var result = db.collection.aggregate([ {$match:{"parent":{$in:["a","b"]}}}, {$sort:{"w":-1}}, {$group:{"_id":"$parent","rec":{$push:"$$ROOT"}}} ]).map(function(doc){ doc.rec.splice(0,3); return doc; }) 

这相当慢,因为每个父代的所有logging都将由MongoDB返回。 select是你的,取决于你的应用程序是什么。

在阅读了一个类似问题的答案之后,我决定沿着这条路走下去,然后我写了一个模块,它为您提供了一定程度的灵活性。

基于我最初的问题的代码示例:

 var _ = require('lodash'); var limited = require('limited'); var D = require('./models/D'); function getLastDsByParent (ids, done) { var options = { model: D, field: 'parent', query: { parent : { $in: ids } }, limit: 3, sort: { w: -1 } }; limited(options, find); function find (err, result) { if (err) { done(err); return; } D .find({ _id: { $in: _.flatten(result, 'documents') } }) .lean() .exec(done); } }