假设我有以下文档结构:
> db.logs.find()
{
'id': ObjectId("50ad8d451d41c8fc58000003")
'name': 'Sample Log 1',
'uploaded_at: ISODate("2013-03-14T01:00:00+01:00"),
'case_id: '50ad8d451d41c8fc58000099',
'tag_doc': {
'group_x: ['TAG-1','TAG-2'],
'group_y': ['XYZ']
}
},
{
'id': ObjectId("50ad8d451d41c8fc58000004")
'name': 'Sample Log 2',
'uploaded_at: ISODate("2013-03-15T01:00:00+01:00"),
'case_id: '50ad8d451d41c8fc58000099'
'tag_doc': {
'group_x: ['TAG-1'],
'group_y': ['XYZ']
}
}
> db.cases.findOne()
{
'id': ObjectId("50ad8d451d41c8fc58000099")
'name': 'Sample Case 1'
}
有没有办法在聚合框架中执行$match
,只检索Log
和case_id
的每个唯一组合的所有最新group_x
?我确信这可以通过多个$group
管道来完成,但是我希望尽可能地立即限制通过$match
运算符传递给管道的文档数量。我想的是像$max
运算符,除了它在$match
中使用。
非常感谢任何帮助。
修改
到目前为止,我可以提出以下建议:
db.logs.aggregate(
{$match: {...}}, // some match filters here
{$project: {tag:'$tag_doc.group_x', case:'$case_id', latest:{uploaded_at:1}}},
{$unwind: '$tag'},
{$group: {_id:{tag:'$tag', case:'$case'}, latest: {$max:'$latest'}}},
{$group: {_id:'$_id.tag', total:{$sum:1}}}
)
正如我所提到的,我想要的是使用多个$group
管道,但是在处理大量文档时这被证明是昂贵的。这就是为什么我想尽早限制文件。
修改
我还没有找到一个好的解决方案,所以我在想如果文档结构本身没有针对我的用例进行优化。我是否必须更新字段以支持我想要实现的目标?建议非常感谢。
修改
我实际上在mongodb中寻找类似于How can I SELECT rows with MAX(Column value), DISTINCT by another column in SQL?中预期的实现,除了它涉及两个不同的字段值。此外,$match
操作至关重要,因为它会使结果集动态化,过滤器范围匹配标记或在一系列日期范围内。
修改
由于我的用例的复杂性,我尝试使用一个简单的类比,但这被证明是令人困惑的。以上是现实用例的简化形式。很抱歉我创造了混乱。
答案 0 :(得分:1)
我做了类似的事情。但是匹配不可能,但只有一个组管道。诀窍是使用多键和正确的排序:
{ user_id: 1, address: "xyz", date_sent: ISODate("2013-03-14T01:00:00+01:00"), message: "test" }, { user_id: 1, address: "xyz2", date_sent: ISODate("2013-03-14T01:00:00+01:00"), message: "test" }
如果我不想分组user_id&地址,我想要创建一个像这样的密钥的最新日期的消息:
{ user_id:1, address:1, date_sent:-1 }
然后你就可以执行不带排序的聚合,这会更快,并且可以在具有副本的分片上运行。如果你没有正确排序顺序的密钥你可以添加一个排序管道,但是你不能将它与分片一起使用,因为所有转移到mongos和分组都是完成它们(也会得到内存限制问题)
db.user_messages.aggregate(
{ $match: { user_id:1 } },
{ $group: {
_id: "$address",
count: { $sum : 1 },
date_sent: { $max : "$date_sent" },
message: { $first : "$message" },
} }
);
没有记录它应该像这样工作 - 但确实如此。我们在生产系统上使用它。
答案 1 :(得分:1)
我会使用另一个集合来动态“创建”搜索结果 - 发布新帖子 - 每次发布新博客帖子时都会在此新集合中插入文档。
每个新的作者/标签组合都会作为新文档添加到此集合中,而具有现有组合的新帖子只会使用新博客帖子的内容(或对象ID参考)更新现有文档。
示例:
db.searchResult.update(
... {'author_id':'50ad8d451d41c8fc58000099', 'tag_doc.tags': ["TAG-1", "TAG-2" ]},
... { $set: { 'Referenceid':ObjectId("5152bc79e8bf3bc79a5a1dd8")}}, // or embed your blog post here
... {upsert:true}
)
答案 2 :(得分:0)
db.posts.aggregate([
{$sort: {created_at:-1}},
{$group: {_id: '$author_id', tags: {$first: '$tag_doc.tags'}}},
{$unwind: '$tags'},
{$group: {_id: {author: '$_id', tag: '$tags'}}}
]);
正如你所说,这不是最佳选择,但这是我想出的全部内容。
如果我是诚实的,如果您需要经常执行此查询,实际上可能更好地预先聚合另一个已经包含您需要的信息的集合:
{
_id: {},
author: {},
tag: 'something',
created_at: ISODate(),
post_id: {}
}
每次您创建新帖子时,您都会在此unqiue集合中搜索所有文档,这些文档会对您所需的内容进行$in
查询,然后更新/上传created_at
和post_id
那个集合。这将更加优化。
答案 3 :(得分:0)
你走了:
db.logs.aggregate(
{"$sort" : { "uploaded_at" : -1 } },
{"$match" : { ... } },
{"$unwind" : "$tag_doc.group_x" },
{"$group" : { "_id" : { "case" :'$case_id', tag:'$tag_doc.group_x'},
"latest" : { "$first" : "$uploaded_at"},
"Name" : { "$first" : "$Name" },
"tag_doc" : { "$first" : "$tag_doc"}
}
}
);
当你可以$ sort并且取$ first时你想要避免$ max,特别是如果你有upload_at的索引,这将允许你避免任何内存排序并显着降低管道处理成本。显然,如果你有其他“数据”字段,你可以将它们与(或代替)“Name”和“tag_doc”一起添加。