【问题标题】:get all the documents having max value using aggregation in mongodb使用 mongodb 中的聚合获取所有具有最大值的文档
【发布时间】:2022-02-09 23:18:21
【问题描述】:

我想获取特定字段的最高值的“所有文档”,而不是按另一个字段分组。

考虑以下数据:

_id:1, country:india,  quantity:12,  name:xyz
_id:2, country:USA,    quantity:5,   name:abc
_id:3, country:USA,    quantity:6,   name:xyz
_id:4, country:india,  quantity:8,   name:def
_id:5, country:USA,    quantity:10,  name:jkl
_id:6, country:india,  quantity:12,  name:jkl

答案应该是

country:india max-quantity:12
name xyz
name jkl 

country:USA max-quantity:10
name jkl

我已经尝试了几个查询,但我只能获得没有名称的最大值,或者我可以分组但它显示了所有值。

db.coll.aggregate([{
    $group:{
        _id:"$country",
        "maxQuantity":{$max:"$quantity"}
    }
}])

例如上面将给出每个国家/地区的最大数量,但是如何与其他字段结合以显示所有最大数量的文档。

【问题讨论】:

  • 在 $group 中的 _id 字段之后添加您的额外字段
  • @AlokDeshwal 你能给我确切的查询吗
  • @AlokDeshwal 这行不通,因为其目的是为每个国家/地区找到一个“最大值”,然后只从“匹配”该“最大值”值的文档中返回其他值。

标签: mongodb mongodb-query aggregation-framework


【解决方案1】:

如果你想保留文档信息,那么你基本上需要将$push它放入一个数组中。但是,当然,有了 $max 值后,您需要过滤数组的内容以仅匹配匹配的元素:

db.coll.aggregate([
    { "$group":{ 
        "_id": "$country",
        "maxQuantity": { "$max": "$quantity" },
        "docs": { "$push": {
            "_id": "$_id",
            "name": "$name",
            "quantity": "$quantity"
        }}
    }},
    { "$project": {
        "maxQuantity": 1,
        "docs": {
            "$setDifference": [
               { "$map": {
                   "input": "$docs",
                   "as": "doc",
                   "in": {
                       "$cond": [ 
                           { "$eq": [ "$maxQuantity", "$$doc.quantity" ] },
                           "$$doc",
                           false
                       ]
                   }
               }},
               [false]
            ]
        }
    }}
])

因此,您将所有内容存储在一个数组中,然后测试每个数组成员以查看它的值是否与记录为最大值的值匹配,丢弃任何不匹配的值。

我会将_id 值保留在数组文档中,因为这使它们“独一无二”,并且在过滤掉值时不会受到$setDifference 的不利影响。但当然,如果“名称”始终是唯一的,那么它就不是必需的。

你也可以从$map返回任何你想要的字段,但我只是返回整个文档。

请记住,这有不超过 16MB 的 BSON 大小限制的限制,因此对于小数据样本是可以的,但任何产生潜在大列表的东西(因为你不能预先过滤数组内容)会更好使用单独的查询处理以查找“最大值”值,并使用另一个查询来获取匹配的文档。

【讨论】:

  • 您的解决方案效果很好,但是对于像我这样的初学者来说有些复杂,因为我不知道 $map , $setDifference ...我仍然会尝试理解并实施它。但是有没有更简单的方法来实现这一点?
  • @ViyatGandhi 看起来很简单是的,使用$unwind 并在再次分组之前使用$match 过滤掉数组,或者只是有条件地使用$sum。但这不是一种“高效”的方式,所以你应该坚持下去。未来的 MongoDB 版本将有一个 $filter 运算符,这会稍微简化一下。关键是了解$map 在做什么,所以请查看链接文档并练习一些示例。如前所述,更大的分组结果无论如何都应该是单独的查询。
  • 这非常复杂。有没有替代的语法?也许不那么冗长?
  • 不知道为什么这是被接受的答案而不是另一个答案 - 正确的答案只是 $sort 和 $group 采取 $first。这太复杂了,而且根本无法扩展。
【解决方案2】:

只有更改特定的国家/地区范围,我才知道如何更简单地完成类似的任务:

[
{"$match":{"name":{"$in":["USA","india"]}}}, // stage one
{ "$sort": { "quanity": -1 }}, // stage three
{"$limit":2 } // stage four - count equal ["USA","india"] length
]

如果您需要所有国家/地区,请尝试关注,但没有我的保证:

[
{"$project": {
    "country": "$country",
    "quantity": "$quantity",
    "document": "$$ROOT" // save all fields for future usage

}},
{ "$sort": { "quantity": -1 }},
{"$group":{"_id":{"country":"$country"},"original_doc":{"$first":"$document"} }}
]

【讨论】:

  • 如果所有国家都需要,尝试 $sort descend,然后 $group 对每个组的第一个匹配进行抽样。
  • 罗勒,你能详细说明一下吗?这是我心目中的策略,但我吐不出来
  • 第二个很棒。谢谢。
  • 这是正确的答案(第二部分,而不是第一部分),但您根本不需要任何类型的 $project。应该只是 $sort 和 $group。
【解决方案3】:

另一种方式可以是:

db.coll.aggregate(
[
  {
    $sort:{ country: -1, "quantity": -1 }
  },
  {
    "$group":
    {
      "_id":{ "country": "$country" },
      "data":{ "$first": "$$ROOT" } 
    }
  }
])

【讨论】:

    【解决方案4】:

    另一种接近 Blakes Seven 的解决方案的可能性,即通过文档数组的过滤器稍微简化 setDifference + map 部分。

    db.coll.aggregate([
        { "$group":{ 
            "_id": "$country",
            "maxQuantity": { "$max": "$quantity" },
            "docs": { "$push": {
                "_id": "$_id",
                "name": "$name",
                "quantity": "$quantity"
            }}
        }},
        { "$project": {
            "maxQuantity": 1,
            "docs": {
                "$filter": {
                  "input": "$docs",
                  "as": "doc",
                  "cond": { $eq: ["$$doc.quantity", "$maxQuantity"] }
                }
            }
        }}
    ])
    

    【讨论】:

      猜你喜欢
      • 2015-01-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-12
      • 1970-01-01
      相关资源
      最近更新 更多