【问题标题】:mongodb find matches based on count aggregationmongodb 根据计数聚合查找匹配项
【发布时间】:2016-03-01 20:18:45
【问题描述】:

我有一个这样的 mongodb 集合:

{"uid": "01370mask4",
 "title": "hidden",
 "post: "hidden",
 "postTime": "01-23, 2016",
 "unixPostTime": "1453538601",
 "upvote": [2, 3]}

我想从帖子超过 5 个的用户中选择帖子记录。结构应该是一样的,我只是不需要帖子不多的用户的文件。

db.collection.aggregate(
   [
     { $group : { _id : "$uid", count: { $sum: 1 } } }
   ]
)

现在我被困在如何使用计数值来查找。我搜索但没有找到任何方法可以通过 uid 将计数值添加回同一集合。 mongodb 似乎不支持保存聚合输出并将它们连接在一起。请指教,谢谢!

更新:

抱歉,我之前没有说清楚。感谢您的及时答复!我想要原始集合的子集,包括发布文本、发布时间戳等。我不想要聚合输出的子集。

【问题讨论】:

  • 我不清楚您的架构中的正确字段名称,我只是在我的答案中使用了一些示例字段...
  • 您能否提供一个示例输入文档和您想要的所需输出?
  • 我认为你原来的问题很清楚,后来的更新很混乱——你怎么能用聚合方法得到帖子细节?您要选择帖子超过 5 个的用户的帖子吗?
  • @SarathNair 谢谢你的建议,我已经更新了。
  • @FrankFang 是的,我想要一个集合的子集,其中包括拥有超过 5 个帖子的用户的帖子记录。

标签: mongodb


【解决方案1】:

如果没有数百万个文档,那么您可以尝试一种快捷方式来实现您正在尝试使用一个聚合和另一个查找查询,

聚合查询:

var users = db.collection.aggregate(
  [
    {$group:{_id:'$uid', count:{$sum:1}}},
    {$match:{count:{$gt:5}}},
    {$group:{_id:null,users:{$push:'$_id'}}}
  ]
).toArray()[0]['users']

然后是查找特定用户的直接查询:

db.collection.find({uid: {$in: users}})

【讨论】:

  • 非常感谢!我不知道toArray 方法并且它有效!我有 18M 文档,在聚合和查找之后,我从中得到了 9M,我在聚合期间使用了allowDiskUse: true,否则它会弹出“内存超出”errmsg。花了一段时间,不是很快,但它确实解决了我的问题。非常感谢!
【解决方案2】:

只需使用正确的查询在您的组后添加$match 即可:

db.collection.aggregate(
  [
    { $group : { _id : "$uid", count: { $sum: 1 } } },
    { $match : { count : { $gt : 5 } }
  ]
)

【讨论】:

    【解决方案3】:

    请尝试使用此选项来选择帖子超过 5 个的用户。使用$first保留原有字段,如果$uid是唯一的,请添加如下字段。

    db.collection.aggregate([
         {$group: {
              _id: '$uid', 
              title: {$first: '$title'}, 
              post: {$first:'$post'}, 
              postTime:{$first: '$postTime'}, 
              unixPostTime:{$first: '$unixPostTime'},
              upvote:{$first: '$upvote'}, 
              count: {$sum: 1}
         }}, 
         {$match: {count: {$gte: 5}}}])
    )
    

    如果同一个$uid 有多个值,则应将$push 用于$group 中的数组。


    如果要将结果保存到db,请尝试如下

    var cur = db.collection.aggregate(
       [
         {$group: {
              _id: '$uid', 
              title: {$first: '$title'}, 
              post: {$first:'$post'}, 
              postTime:{$first: '$postTime'}, 
              unixPostTime:{$first: '$unixPostTime'},
              upvote:{$first: '$upvote'}, 
              count: {$sum: 1}
         }}, 
         {$match: {count: {$gte: 5}}}
       ]
    )
    cur.forEach(function(doc) {
       db.collectioin.update({_id: doc._id}, {/*the field should be updated */});
    });
    

    【讨论】:

    • 谢谢!我试过db.collection.aggregate( [ {$group: { _id: "$uid", title: "$title", post: "$post", postTime: "$postTime", unixPostTime: "$unixPostTime", upvote: "$upvote", count: {$sum: 1} } }, {$match: {count: {$gt: 5} } } ] ),但一直失败:"errmsg" : "exception: the group aggregate field 'title' must be defined as an expression inside an object", "code" : 15951, "ok" : 0
    • 感谢您一直以来的建议!我认为$firstcount 有冲突,因为$first 仅适用于拥有1 个帖子的用户。虽然$push 方法看起来很实用,但它会创建一个类似merging two collections in mongodb 的嵌套文档,但我仍然想保留原始结构。这就是为什么我没有遵循合并方法并且我说“mongodb不支持将它们连接在一起(原始和聚合输出)”。
    • @leoce,是的,$first 只是为您的文档提供独特的uid。但是,如果同一个uid 有多个文档,则此处应使用$push
    • $push 为一个唯一用户创建一个文档,她/他的帖子成为其中的子文档。但是,我想保留原始结构并制作例如 5 个帖子分开。我仍在寻找是否有某种方法可以更新原始集合。
    • @leoce,据我所知,目前的 mongodb 版本没有更好的解决方案。如果你有另一个好的解决方案。请让我得到它...
    猜你喜欢
    • 2020-06-29
    • 1970-01-01
    • 1970-01-01
    • 2022-06-16
    • 2020-09-20
    • 2023-02-25
    • 1970-01-01
    • 2021-12-07
    • 1970-01-01
    相关资源
    最近更新 更多