Mongodb 聚合前为唯一字段集匹配最新文档
假设我有以下文档结构:Mongodb 聚合前为唯一字段集匹配最新文档,mongodb,aggregation-framework,Mongodb,Aggregation Framework,假设我有以下文档结构: > 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': ['
> 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
,对于案例id
和组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中寻找一个类似于中预期的实现,只是它涉及两个不同的字段值。另外,$match
操作也很关键,因为它使生成的集合具有动态性,过滤器的范围包括匹配的标记或日期范围
编辑:
由于我的用例的复杂性,我尝试使用一个简单的类比,但这被证明是令人困惑的。以上是实际用例的简化形式。很抱歉我造成了混乱。我也做了类似的事情。但这是不可能的比赛,但只有一组管道。诀窍是使用具有正确排序的多键:
{ 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" }
如果我不想在用户id和地址上分组,也不想在邮件中显示最新日期,我们需要创建一个密钥,如下所示:
{ user_id:1, address:1, date_sent:-1 }
然后,您就可以执行聚合而无需排序,这会更快,并且可以处理具有副本的碎片。如果您没有具有正确排序顺序的密钥,则可以添加排序管道,但不能将其用于碎片,因为所有传输到mongos和分组的操作都已完成(也会出现内存限制问题)
没有文件证明它应该是这样工作的——但确实如此。我们在生产系统中使用它。Hmmm,没有一种最佳的方法可以做到这一点,即您只需要挑选每个作者的最新版本,而需要挑选所有文档,进行排序,然后按作者分组:
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集合中的所有文档,这些文档在查询中填写您需要的内容,然后更新/上传在创建的
和发布id
。这将更为理想。我会使用另一个集合来动态“创建”搜索结果——在发布新文章时——每次发布新博客文章时,都会在这个新集合中增加一个文档
作者/标记的每一个新组合都会作为新文档添加到此集合中,而具有现有组合的新帖子只会使用新博客帖子的内容(或对象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}
)
给你:
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进行排序并首先获取$max时,您希望避免使用$max,特别是如果您在上传的\u上有一个索引,该索引允许您避免任何内存中的排序,并显著降低管道处理成本。显然,如果您有其他“数据”字段,您会将它们与“名称”和“tag_doc”一起添加(或代替它们)。一些评论:我正在考虑首先执行$project
,以限制将进入管道的字段。另外,我的标记
字段是一个数组,因此我必须执行$unwind
对吗?如果我错了,请纠正我,但在这些操作之后,索引不再可用,不是吗?是的,如果不需要所有字段,则应该使用$project pipline来节省内存。是的,您将需要一个$unwind管道-尚未测试您是否可以使用此密钥-您应该尝试一下。从中可以看出,我可能无法在这些操作中利用索引。我忘了提到,在我的实际模式中,“tags”数组位于嵌入式文档中,我需要$project
将其移动到文档的第一级。我尝试了您的建议,但我发现,使用您提供的示例,它只会按地址对用户\u消息进行分组,但每个计数仍然包括每个组中较旧的消息s。样本结构已更新以匹配实际文档结构。对此问题有何建议?问题是预聚合将很难实现,因为在我的用例中分组是动态的,用户最初可以通过多种方式过滤帖子
,例如,在一个日期范围内,匹配一组标签等。因此,一个组的最新帖子可能是相对的(我希望你明白了)。@nyde1319为什么需要文档输出?你就不能根据作者最新帖子中的标签来匹配吗?为什么每个作者和标签都需要输出非预期文档?在我的用例中,用户可以对一组帖子执行过滤。现在,用户还可以选择根据一组标记对结果进行分组。在我的示例中,结果可以按tag的不同值分组_
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"}
}
}
);