3

假设我有以下文档结构:

> 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_idgroup_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 中的实现,类似于我如何在 SQL 中的另一列中选择具有 MAX(列值)、DISTINCT 的行?除了它涉及两个不同的字段值。此外,该$match操作至关重要,因为它使结果集动态化,过滤器范围为匹配标签或日期范围内。

编辑:

由于我的用例的复杂性,我尝试使用一个简单的类比,但这被证明是令人困惑的。以上是实际用例的简化形式。对我造成的混乱感到抱歉。

4

4 回答 4

1

I have done something similar. But it's not possible with match, but only with one group pipeline. The trick is do use multi key with correct sorting:

   { 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" }

if i wan't to group on user_id & address and i wan't the message with the latest date we need to create a key like this:

{ user_id:1, address:1, date_sent:-1 }

then you are able to perform aggregate without sort, which is much faster and will work on shards with replicas. if you don't have a key with correct sort order you can add a sort pipeline, but then you can't use it with shards, because all that is transferred to mongos and grouping is done their (also will get memory limit problems)

 db.user_messages.aggregate(
 { $match: { user_id:1 } },
 { $group: {
     _id: "$address",
     count: { $sum : 1 },
     date_sent: { $max : "$date_sent" },
     message: { $first : "$message" },
 } }
);

It's not documented that it should work like this - but it does. We use it on production system.

于 2013-03-18T23:10:46.553 回答
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}
)
于 2013-03-27T08:08:36.220 回答
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 上有一个索引,这将允许您避免任何内存排序并显着降低管道处理成本。显然,如果您有其他“数据”字段,您可以将它们与(或代替)“名称”和“tag_doc”一起添加。

于 2013-09-01T21:42:04.923 回答
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: {}
}

每次创建新帖子时,您都会在这个独特的集合中查找所有文档,这些文档可以满足您对所需内容的查询,$in然后更新/更新该集合。这将是更优化的。created_atpost_id

于 2013-03-26T08:02:43.343 回答