如何使用获得 N 个结果的 Mongoose 进行查询,但结合它找到的满足特定条件的任何文档?
How to make a query using Mongoose that gets N results, but combines any documents it finds that meet certain criteria?
我在 Mongoose 中有一个 Comments 集合,还有一个 return 最近五个(任意数量)评论的查询。
每条评论都与另一个文档相关联。我想做的是查询 return 最近的 5 条评论, 评论与同一其他文档相关联。
所以不是这样的列表:
results = [
{ _id: 123, associated: 12 },
{ _id: 122, associated: 8 },
{ _id: 121, associated: 12 },
{ _id: 120, associated: 12 },
{ _id: 119, associated: 17 }
]
我想要 return 这样的列表:
results = [
{ _id: 124, associated: 3 },
{ _id: 125, associated: 19 },
[
{ _id: 123, associated: 12 },
{ _id: 121, associated: 12 },
{ _id: 120, associated: 12 },
],
{ _id: 122, associated: 8 },
{ _id: 119, associated: 17 }
]
请不要太担心数据格式:它只是一个草图,试图展示我想要的那种东西。我想要一个指定大小的结果集,但其中一些结果根据某些标准分组。
显然,执行此操作的一种方法是只进行查询、抓取和修改结果,然后递归地再次进行查询,直到结果集达到所需的长度。那种方式看起来很尴尬。有没有更好的方法来解决这个问题?我无法在 Google 搜索中以一种让我接近任何可能有洞察力的人的方式来表达它。
这是一个聚合管道查询,可以满足您的要求:
db.comments.aggregate([
{ $group: { _id: "$associated", maxID: { $max: "$_id"}, cohorts: { $push: "$$ROOT"}}},
{ $sort: { "maxID": -1 } },
{ $limit: 5 }
])
示例数据中缺少任何其他字段作为排序依据,我使用了 $_id。
如果您希望结果在结构上更接近您提供的示例结果集,您可以在末尾添加一个 $project
:
db.comments.aggregate([
{ $group: { _id: "$associated", maxID: { $max: "$_id"}, cohorts: { $push: "$$ROOT"}}},
{ $sort: { "maxID": -1 } },
{ $limit: 5 },
{ $project: { _id: 0, cohorts: 1 }}
])
这将只打印结果集。请注意,即使不共享关联对象的评论也将位于一个数组中。它将是一个长度为 1 的数组。
如果您担心像 Neil Lunn 所建议的那样限制分组中的结果,也许开始时 $match
是一个明智的主意。
db.comments.aggregate([
{ $match: { createDate: { $gte: new Date(new Date() - 5 * 60000) } } },
{ $group: { _id: "$associated", maxID: { $max: "$_id"}, cohorts: { $push: "$$ROOT"}}},
{ $sort: { "maxID": -1 } },
{ $limit: 5 },
{ $project: { _id: 0, cohorts: 1 }}
])
如果您有 createDate
类型字段,那将只包括最近 5 分钟内发表的评论。如果这样做,您也可以考虑使用它作为排序依据的字段而不是“_id”。如果您没有 createDate
类型字段,我不确定如何最好地限制分组的评论,因为我不知道 "current _id" 有 [=30] =].
老实说,我认为你在这里问了很多,我自己并不能真正看到实用程序,但如果我遗漏了一些有用的东西,我总是很乐意向我解释。
底线是您希望按日期来自最后五个不同用户的评论,然后对这些用户的其他评论进行某种分组。最后一部分是我认为规则有困难的地方,无论你想如何攻击它,但我会尽量保持最简短的形式。
在任何类型的单个查询中都不可能发生这种情况。但是可以做一些事情来使其成为高效的服务器响应:
var DataStore = require('nedb'),
store = new DataStore();
async.waterfall(
function(callback) {
Comment.aggregate(
[
{ "$match": { "postId": thisPostId } },
{ "$sort": { "associated": 1, "createdDate": -1 } },
{ "$group": {
"_id": "$associated",
"date": { "$first": "$createdDate" }
}},
{ "$sort": { "date": -1 } },
{ "$limit": 5 }
],
callback);
},
function(docs,callback) {
async.each(docs,function(doc,callback) {
Comment.aggregate(
[
{ "$match": { "postId": thisPostId, "associated": doc._id } },
{ "$sort": { "createdDate": -1 } },
{ "$limit": 5 },
{ "$group": {
"_id": "$associated",
"docs": {
"$push": {
"_id": "$_id", "createdDate": "$createdDate"
}
},
"firstDate": { "$first": "$createdDate" }
}}
],
function(err,results) {
if (err) callback(err);
async.each(results,function(result,callback) {
store.insert( result, function(err, result) {
callback(err);
});
},function(err) {
callback(err);
});
}
);
},
callback);
},
function(err) {
if (err) throw err;
store.find({}).sort({ "firstDate": - 1 }).exec(function(err,docs) {
if (err) throw err;
console.log( JSON.stringify( docs, undefined, 4 ) );
});
}
);
现在我在文档和数组中添加了更多的文档属性,但是基于您的示例的简化形式会像这样出现:
results = [
{ "_id": 3, "docs": [124] },
{ "_id": 19, "docs": [125] },
{ "_id": 12, "docs": [123,121,120] },
{ "_id": 8, "docs": [122] },
{ "_id": 17, "docs": [119] }
]
所以基本的想法是首先通过基本上砍掉最后 5 个来找到你独特的 "users" 最后评论的人。在这里不过滤某种范围会遍历整个集合以获得那些结果,所以最好以某种方式限制它,比如在最后一个小时或最后几个小时或根据需要采取合理的措施。只需将这些条件添加到 $match
以及与评论关联的当前 post。
一旦你有了这 5 个,那么你想获得这些用户的多个评论的任何可能的 "grouped" 详细信息。同样,通常建议对时间范围进行某种限制,但作为一般情况,这只是查找用户对当前 post 的最新评论并将其限制为 5.
这里的执行是并行完成的,这将使用更多资源,但考虑到只有 5 个查询 运行 还是相当有效的。与您的示例输出相反,此处的数组位于文档结果内部,它包含每个评论的原始文档 id 值以供参考。与文档相关的任何其他内容以及所需内容(即评论的内容)都将被推送到数组中。
这里的另一个小技巧是使用 nedb as a means for storing the output of each query in an "in memory" collection. This need only really be a standard hash data structure, but nedb 为您提供了一种方法,同时保持您可能习惯的 MongoDB 语句形式。
获得所有结果后,您只需 return 将它们作为输出,并如图所示进行排序以保留最后评论者的顺序。实际的评论被分组在每一项的数组中,你可以遍历它来输出你喜欢的。
这里的底线是您要求 "top N results problem" 的复合版本,这是 MongoDB 经常被问到的问题。我之前写过解决这个问题的方法,以展示在单个聚合管道阶段如何实现这一点,但除了相对较小的结果集之外,它真的不实用。
如果您真的想参与其中,那么您可以查看 Mongodb aggregation $group, restrict length of array 以获得更详细的示例之一。但是为了我的钱,我每天都会 运行 进行并行查询。 Node.js 有合适的环境来支持他们,否则你会很疯狂。
我在 Mongoose 中有一个 Comments 集合,还有一个 return 最近五个(任意数量)评论的查询。
每条评论都与另一个文档相关联。我想做的是查询 return 最近的 5 条评论, 评论与同一其他文档相关联。
所以不是这样的列表:
results = [
{ _id: 123, associated: 12 },
{ _id: 122, associated: 8 },
{ _id: 121, associated: 12 },
{ _id: 120, associated: 12 },
{ _id: 119, associated: 17 }
]
我想要 return 这样的列表:
results = [
{ _id: 124, associated: 3 },
{ _id: 125, associated: 19 },
[
{ _id: 123, associated: 12 },
{ _id: 121, associated: 12 },
{ _id: 120, associated: 12 },
],
{ _id: 122, associated: 8 },
{ _id: 119, associated: 17 }
]
请不要太担心数据格式:它只是一个草图,试图展示我想要的那种东西。我想要一个指定大小的结果集,但其中一些结果根据某些标准分组。
显然,执行此操作的一种方法是只进行查询、抓取和修改结果,然后递归地再次进行查询,直到结果集达到所需的长度。那种方式看起来很尴尬。有没有更好的方法来解决这个问题?我无法在 Google 搜索中以一种让我接近任何可能有洞察力的人的方式来表达它。
这是一个聚合管道查询,可以满足您的要求:
db.comments.aggregate([
{ $group: { _id: "$associated", maxID: { $max: "$_id"}, cohorts: { $push: "$$ROOT"}}},
{ $sort: { "maxID": -1 } },
{ $limit: 5 }
])
示例数据中缺少任何其他字段作为排序依据,我使用了 $_id。
如果您希望结果在结构上更接近您提供的示例结果集,您可以在末尾添加一个 $project
:
db.comments.aggregate([
{ $group: { _id: "$associated", maxID: { $max: "$_id"}, cohorts: { $push: "$$ROOT"}}},
{ $sort: { "maxID": -1 } },
{ $limit: 5 },
{ $project: { _id: 0, cohorts: 1 }}
])
这将只打印结果集。请注意,即使不共享关联对象的评论也将位于一个数组中。它将是一个长度为 1 的数组。
如果您担心像 Neil Lunn 所建议的那样限制分组中的结果,也许开始时 $match
是一个明智的主意。
db.comments.aggregate([
{ $match: { createDate: { $gte: new Date(new Date() - 5 * 60000) } } },
{ $group: { _id: "$associated", maxID: { $max: "$_id"}, cohorts: { $push: "$$ROOT"}}},
{ $sort: { "maxID": -1 } },
{ $limit: 5 },
{ $project: { _id: 0, cohorts: 1 }}
])
如果您有 createDate
类型字段,那将只包括最近 5 分钟内发表的评论。如果这样做,您也可以考虑使用它作为排序依据的字段而不是“_id”。如果您没有 createDate
类型字段,我不确定如何最好地限制分组的评论,因为我不知道 "current _id" 有 [=30] =].
老实说,我认为你在这里问了很多,我自己并不能真正看到实用程序,但如果我遗漏了一些有用的东西,我总是很乐意向我解释。
底线是您希望按日期来自最后五个不同用户的评论,然后对这些用户的其他评论进行某种分组。最后一部分是我认为规则有困难的地方,无论你想如何攻击它,但我会尽量保持最简短的形式。
在任何类型的单个查询中都不可能发生这种情况。但是可以做一些事情来使其成为高效的服务器响应:
var DataStore = require('nedb'),
store = new DataStore();
async.waterfall(
function(callback) {
Comment.aggregate(
[
{ "$match": { "postId": thisPostId } },
{ "$sort": { "associated": 1, "createdDate": -1 } },
{ "$group": {
"_id": "$associated",
"date": { "$first": "$createdDate" }
}},
{ "$sort": { "date": -1 } },
{ "$limit": 5 }
],
callback);
},
function(docs,callback) {
async.each(docs,function(doc,callback) {
Comment.aggregate(
[
{ "$match": { "postId": thisPostId, "associated": doc._id } },
{ "$sort": { "createdDate": -1 } },
{ "$limit": 5 },
{ "$group": {
"_id": "$associated",
"docs": {
"$push": {
"_id": "$_id", "createdDate": "$createdDate"
}
},
"firstDate": { "$first": "$createdDate" }
}}
],
function(err,results) {
if (err) callback(err);
async.each(results,function(result,callback) {
store.insert( result, function(err, result) {
callback(err);
});
},function(err) {
callback(err);
});
}
);
},
callback);
},
function(err) {
if (err) throw err;
store.find({}).sort({ "firstDate": - 1 }).exec(function(err,docs) {
if (err) throw err;
console.log( JSON.stringify( docs, undefined, 4 ) );
});
}
);
现在我在文档和数组中添加了更多的文档属性,但是基于您的示例的简化形式会像这样出现:
results = [
{ "_id": 3, "docs": [124] },
{ "_id": 19, "docs": [125] },
{ "_id": 12, "docs": [123,121,120] },
{ "_id": 8, "docs": [122] },
{ "_id": 17, "docs": [119] }
]
所以基本的想法是首先通过基本上砍掉最后 5 个来找到你独特的 "users" 最后评论的人。在这里不过滤某种范围会遍历整个集合以获得那些结果,所以最好以某种方式限制它,比如在最后一个小时或最后几个小时或根据需要采取合理的措施。只需将这些条件添加到 $match
以及与评论关联的当前 post。
一旦你有了这 5 个,那么你想获得这些用户的多个评论的任何可能的 "grouped" 详细信息。同样,通常建议对时间范围进行某种限制,但作为一般情况,这只是查找用户对当前 post 的最新评论并将其限制为 5.
这里的执行是并行完成的,这将使用更多资源,但考虑到只有 5 个查询 运行 还是相当有效的。与您的示例输出相反,此处的数组位于文档结果内部,它包含每个评论的原始文档 id 值以供参考。与文档相关的任何其他内容以及所需内容(即评论的内容)都将被推送到数组中。
这里的另一个小技巧是使用 nedb as a means for storing the output of each query in an "in memory" collection. This need only really be a standard hash data structure, but nedb 为您提供了一种方法,同时保持您可能习惯的 MongoDB 语句形式。
获得所有结果后,您只需 return 将它们作为输出,并如图所示进行排序以保留最后评论者的顺序。实际的评论被分组在每一项的数组中,你可以遍历它来输出你喜欢的。
这里的底线是您要求 "top N results problem" 的复合版本,这是 MongoDB 经常被问到的问题。我之前写过解决这个问题的方法,以展示在单个聚合管道阶段如何实现这一点,但除了相对较小的结果集之外,它真的不实用。
如果您真的想参与其中,那么您可以查看 Mongodb aggregation $group, restrict length of array 以获得更详细的示例之一。但是为了我的钱,我每天都会 运行 进行并行查询。 Node.js 有合适的环境来支持他们,否则你会很疯狂。