关联的 Mongoose 限制
Mongoose limit by association
我有一个 collection 这样的:
[
{ 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 }
]
给定具有相关 parent ID 的查询,['b','c']
,我需要为每个 parent 取回前 3 个结果,希望 DESC-sorted [=13] =]:
[
{ 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()
会 return 总体上的前 N 个结果,而不是每个 parent
的前 N 个结果。使用 .aggregate()
我想出了如何通过 parent
进行聚合,但我无法弄清楚如何通过 parent 进行 $limit
,也不知道如何 return 整个文档作为 {parent: 'b', items: [{..}, {..}] }
而不仅仅是组数据。我可以得到我已经拥有的 parent
,或者 parent
和某个字段上使用 $push
的数组,但这仍然不好。
最后我也尝试了 .mapReduce
但这似乎有点矫枉过正,难道我不必为聚合部分 emit(this.project, this);
吗?我怎么会对此有限制?用手?它的文档很少。
无论如何,在这里找到前进的方向会很棒。我正在使用 mongoose@latest
.
作为 pointed,不幸的是,这无法使用当前存在的 MongoDB
的聚合框架来实现,而且正如您提到的那样,map-reduce
将是一种矫枉过正。
但还有其他方法:
方法一:
- 根据
w
维护表示层次结构级别的变量
字段,或要对结果集进行排序的字段。一次
您在插入期间将变量添加到每个文档。
- 您的文档将包含一个名为
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] }
])
假设您希望根据每个 parent 的 w
字段的排序顺序获得前 3
个结果。您可以轻松汇总如下:
var levels = [1,2,3]; // indicating the records in the range that we need to pick up,
// from each parent.
- 匹配
a
或 b
的所有 parent。
- 按
w
字段对记录进行排序。
- 按
parent
分组。分组后,parent 的所有文档
成为分组记录的子文档,因此允许您
应用 $redact 阶段。
- 现在应用
$redact
阶段来编辑那些子文档,其
级别不是我们寻求的级别的子集。我们将 level
保留为
一个数组,因为它可以更容易地应用 $setIsSubset
运营商就可以了。否则我们会要求 $in
,这不是
在 $cond
表达式中支持。
代码:
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;
})
这相当慢,因为每个 parent 的所有记录都将由 MongoDB
返回。选择权在您,取决于适合您的应用程序。
看完后this answer to a similar question I decided to go down that path, and I wrote a module that builds the aggregate query for you具有一定的灵活性
基于我最初问题的示例代码:
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);
}
}
我有一个 collection 这样的:
[
{ 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 }
]
给定具有相关 parent ID 的查询,['b','c']
,我需要为每个 parent 取回前 3 个结果,希望 DESC-sorted [=13] =]:
[
{ 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()
会 return 总体上的前 N 个结果,而不是每个 parent
的前 N 个结果。使用 .aggregate()
我想出了如何通过 parent
进行聚合,但我无法弄清楚如何通过 parent 进行 $limit
,也不知道如何 return 整个文档作为 {parent: 'b', items: [{..}, {..}] }
而不仅仅是组数据。我可以得到我已经拥有的 parent
,或者 parent
和某个字段上使用 $push
的数组,但这仍然不好。
最后我也尝试了 .mapReduce
但这似乎有点矫枉过正,难道我不必为聚合部分 emit(this.project, this);
吗?我怎么会对此有限制?用手?它的文档很少。
无论如何,在这里找到前进的方向会很棒。我正在使用 mongoose@latest
.
作为 pointed,不幸的是,这无法使用当前存在的 MongoDB
的聚合框架来实现,而且正如您提到的那样,map-reduce
将是一种矫枉过正。
但还有其他方法:
方法一:
- 根据
w
维护表示层次结构级别的变量 字段,或要对结果集进行排序的字段。一次 您在插入期间将变量添加到每个文档。 - 您的文档将包含一个名为
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] }
])
假设您希望根据每个 parent 的 w
字段的排序顺序获得前 3
个结果。您可以轻松汇总如下:
var levels = [1,2,3]; // indicating the records in the range that we need to pick up,
// from each parent.
- 匹配
a
或b
的所有 parent。 - 按
w
字段对记录进行排序。 - 按
parent
分组。分组后,parent 的所有文档 成为分组记录的子文档,因此允许您 应用 $redact 阶段。 - 现在应用
$redact
阶段来编辑那些子文档,其 级别不是我们寻求的级别的子集。我们将level
保留为 一个数组,因为它可以更容易地应用$setIsSubset
运营商就可以了。否则我们会要求$in
,这不是 在$cond
表达式中支持。
代码:
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;
})
这相当慢,因为每个 parent 的所有记录都将由 MongoDB
返回。选择权在您,取决于适合您的应用程序。
看完后this answer to a similar question I decided to go down that path, and I wrote a module that builds the aggregate query for you具有一定的灵活性
基于我最初问题的示例代码:
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);
}
}