【问题标题】:MongoDB - Aggregate by distinct field then count per dayMongoDB - 按不同字段聚合然后每天计数
【发布时间】:2019-06-14 11:23:36
【问题描述】:

我有一个收集设备数据的 mongodb 数据库。 示例文档是

{
    "_id" : ObjectId("5c125a185dea1b0252c5352"),
    "time" : ISODate("2018-12-13T15:09:42.536Z"),
    "mac" : "10:06:21:3e:0a:ff",
}

目标是每天计算唯一的 mac 值,从数据库中的第一个文档到数据库中的最后一个文档。

我一直在玩,得出的结论是,在我的聚合过程中,我需要有多个组和项目。

这是我尝试过的 - 不确定方向是否正确或完全搞砸了。

   pipeline = [
        {"$project": {
            "_id": 1,
            "mac": 1,
            "day": {
                "$dayOfMonth":"$time"
            },
            "month": {
                "$month":"$time"
            },
            "year": {
                "$year":"$time"
            }
        }
        },
        {
        "$project": {
            "_id": 1,
            "mac": 1,
            "time": {
                "$concat": [{
                    "$substr":["$year", 0, 4]
                },
                "-", {
                    "$substr": ["$month", 0, 2]

                },
                "-",
                {
                    "$substr":["$day", 0, 2]
                }]
                }
            }
        },
        {
            "$group": {
                "_id": {
                    "time": "$time",
                    "mac": "$mac"
                }
            },
            "$group": {
                "_id": "$_id.time",
                "count":{"$sum": 1},
            }
        }
    ]

    data = list(collection.aggregate(pipeline, allowDiskUse=True))

现在的输出看起来不像进行任何聚合,

[{"_id": null, "count": 751050}]

我使用 Pymongo 作为我的驱动程序并使用 Mongodb 4。

理想情况下,它应该只显示日期和计数(例如 { "_id" : "2018-12-13", "count" : 2 }。

我希望得到一些反馈和建议。 提前致谢。

【问题讨论】:

  • 你的想法是对的,你只是在语法上把第二个 $group 放在了错误的地方——当它应该是下一个阶段时,你把它嵌入到了第一个 $group 中。
  • 我添加了一个答案,展示了如何使用单个 $group 阶段(以及将输出转换为所需格式的第二阶段)来做到这一点

标签: mongodb pymongo aggregation


【解决方案1】:

我更喜欢尽量减少阶段的数量,尤其是避免不必要的 $group 阶段。所以我会用以下管道来做:

pipeline = [ 
  { '$group' : {
      '_id': { '$dateToString': { 'format': "%Y-%m-%d", 'date': "$time" } },
      'macs':{ '$addToSet': '$mac' }
   } },
   {$addFields:{ 'macs':{'$size':'$macs'}}}
]

【讨论】:

  • 哇,这比增加 2 个小组赛阶段效率高得多
【解决方案2】:

有一个叫做“$dateToString”的操作符,可以解决你的大部分问题。

编辑:没有仔细阅读问题,@Asya Kamsky,谢谢你的指出。这是新的答案。

  pipeline = [
  {
    "$group": {
      "_id": {
        "date": {
          $dateToString: {
            format: "%Y-%m-%d",
            date: "$time"
          }
        },
        "mac": "$mac"
      }
    }
  },
  {
    "$group": {
      "_id": "$_id.date",
      "count": {
        "$sum": 1
      }
    }
  }
]

【讨论】:

  • 这是错误的 - 它不会为您提供每天唯一设备的数量,而是为您提供每天多次计算相同设备的设备总数。
  • @AsyaKamsky 我不仔细阅读问题,编辑了答案。
【解决方案3】:
[
                {
            "$project": {
                "_id": 1,
                "mac": 1,
                "time": { "$dateToString": { "format": "%Y-%m-%d", "date": "$time", "timezone": "Africa/Johannesburg"}}
            },
        },

        {
            "$group": {
                "_id":{
                "time": "$time",
                "mac": "$mac",
            }}},{
            "$group": {
                "_id": "$_id.time",
                "count":{"$sum": 1}
            }},
         {"$sort": SON([("_id", -1)])}

]

做它应该做的事情。 谢谢。 :)

【讨论】:

  • 这个可以正常工作,但是不需要$project阶段,计算时间的表达式可以直接在$group阶段使用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-01
  • 2018-08-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-08
相关资源
最近更新 更多