如何将数组元素与聚合中的最大值进行匹配和分组

时间:2016-04-07 09:12:30

标签: mongodb mongodb-query aggregation-framework

我需要帮助才能使数组元素具有文档中字段(级别)的最大值。然后计算按数组元素字段“bssid”分组的总出现次数。

考虑以下数据

/* 1 */
{
    "_id" : "18:59:36:0c:94:a3",
    "timestamp" : "1460012567",
    "apdata" : [{
        "bssid" : "f4:b7:e2:56:e4:20",
        "ssid" : "Test Network2",
        "level" : -55
    }, {
        "bssid" : "b8:a3:86:67:03:56",
        "ssid" : "Test Network1",
        "level" : -76
    }]
}
/* 2 */
{
    "_id" : "d0:b3:3f:b9:42:38",
    "timestamp" : "1460013345",
    "apdata" : [{
        "bssid" : "f4:b7:e2:56:e4:20",
        "ssid" : "Test Network2",
        "level" : -65
    }, {
        "bssid" : "b8:a3:86:67:03:56",
        "ssid" : "Test Network1",
        "level" : -46
    }]
}
/* 3 */
{
    "_id" : "d0:b3:3f:b9:42:41",
    "timestamp" : "1460013145",
    "apdata" : [{
        "bssid" : "f4:b7:e2:56:e4:20",
        "ssid" : "Test Network2",
        "level" : -65
    }, {
        "bssid" : "b8:a3:86:67:03:56",
        "ssid" : "Test Network1",
        "level" : -46
    }]
 }

所需的输出是

{
    "bssid" : "f4:b7:e2:56:e4:20",
    "ssid" : "Test Network2",
    "count" : 1
}, {
    "bssid" : "b8:a3:86:67:03:56",
    "ssid" : "Test Network1",
    "count" : 2
}

每个bssid在整个集合中每个文档的数组中具有最大值的次数。

1 个答案:

答案 0 :(得分:1)

如果您有MongoDB 3.2,那么您可以这样做:

db.sample.aggregate([
  { "$project": {
    "apdata": {
      "$arrayElemAt": [
        { "$filter": {
          "input": "$apdata",
          "as": "el",
          "cond": {
            "$eq": [ 
              "$$el.level",
              { "$max": {
                "$map": {
                  "input": "$apdata",
                  "as": "data",
                  "in": "$$data.level"
                }
              }}
            ]
          }
        }},
        0
      ]
    }
  }},
  { "$group": {
    "_id": "$apdata.bssid",
    "ssid": { "$first": "$apdata.ssid" },
    "count": { "$sum": 1 }
  }}
])

至少对于MongoDB 2.6,你需要这样做:

db.sample.aggregate([
  { "$unwind": "$apdata" },
  { "$group": {
    "_id": "$_id",
    "apdata": { "$push": "$apdata" },
    "max": { "$max": "$apdata.level" }
  }},
  { "$unwind": "$apdata" },
  { "$redact": {
    "$cond": {
      "if": { "$eq": [ "$apdata.level", "$max" ] },
      "then": "$$KEEP",
      "else": "$$PRUNE"
    }
  }},
  { "$group": {
    "_id": "$apdata.bssid",
    "ssid": { "$first": "$apdata.ssid" },
    "count": { "$sum": 1 }
  }}
])

对于像这样的MongoDB 2.4或2.2:

db.sample.aggregate([
  { "$unwind": "$apdata" },
  { "$group": {
    "_id": "$_id",
    "apdata": { "$push": "$apdata" },
    "max": { "$max": "$apdata.level" }
  }},
  { "$unwind": "$apdata" },
  { "$project": {
    "apdata": 1,
    "isMax": { "$eq": [ "$apdata.level", "$max" ] }
  }},
  { "$match": { "isMax": true } },
  { "$group": {
    "_id": "$apdata.bssid",
    "ssid": { "$first": "$apdata.ssid" },
    "count": { "$sum": 1 }
  }}
])

在所有情况下,$max用于获取每个文档中“数组”中数组的“最大”值,然后您可以使用它来“过滤”数组内容,然后再将其用于{ {3}}。这种方法仅随版本

而变化

MongoDB 3.2 :允许$group直接处理值的“数组”。因此,$max用于获取"level"值并找出“max”实际上是什么。

然后$map可用于返回与“max”值匹配的数组元素,最后$filter用于返回“仅”(两个可能的“0”) “index”元素作为普通文档。

如果您基本上重复_id的整个陈述并获得$arrayElemAt "ssid"值,则整个过程可以仅在$group“中完成” ,但要分别用$first来演示来表示更容易一些。

MongoDB 2.6 :这缺乏更高级的运营商,尤其是$project能够“直接”在阵列上工作。值得注意的是,首先需要$max数组,然后才能在原始文档中实际$unwind,只是为了获得“最大”值。

然后,该过程确实需要您再次$group,因为您稍后将对数组中的元素进行分组,然后使用$unwind来过滤内容。这是$redact的“逻辑”形式,您可以直接将"level"与早期阶段的计算“max”进行比较。因此,不会删除不是“max”的元素。

MongoDB 2.4 :基本上是相同的逻辑,除了代替$match,你实际上需要物理$redact才能在文档中放置一个字段用于使用$project进行过滤。

所有版本都具有相同的最终$match,您可以在其中为"apdata.bssid"提供"ssid"的路径,并为{ "_id" : "f4:b7:e2:56:e4:20", "ssid" : "Test Network2", "count" : 1 } { "_id" : "b8:a3:86:67:03:56", "ssid" : "Test Network1", "count" : 2 } db.sample.aggregate([ { "$group": { "_id": { "$arrayElemAt": [ { "$map": { "input": { "$filter": { "input": "$apdata", "as": "el", "cond": { "$eq": [ "$$el.level", { "$max": { "$map": { "input": "$apdata", "as": "data", "in": "$$data.level" } }} ] } } }, "as": "apdata", "in": { "bssid": "$$apdata.bssid", "ssid": "$$apdata.ssid" } }}, 0 ] }, "count": { "$sum": 1 } }} ]) 的分组边界提供$group结果一个简单的$first来计算结果中分组键的出现次数。

一切都返回如下:

_id

实际上,MongoDB 3.2最“高效”的形式如下:

  {
    "_id" : {
      "bssid" : "b8:a3:86:67:03:56",
      "ssid" : "Test Network1"
    },
    "count" : 2
  }
  {
    "_id" : {
      "bssid" : "f4:b7:e2:56:e4:20",
      "ssid" : "Test Network2"
    },
    "count" : 1
  }

由于化合物{{1}}的形式略有不同,但它只是一个$sum阶段,没有重复整个过程来查找“max”值的数组元素数据:

{{1}}