【问题标题】:How to merge array field in document in Mongo aggregation如何在 Mongo 聚合中合并文档中的数组字段
【发布时间】:2015-01-10 08:33:21
【问题描述】:

我有一个要求,我需要对两条记录进行聚合,这两条记录都有一个具有不同值的数组字段。我需要的是,当我对这些记录进行聚合时,结果应该有一个数组,该数组具有来自两个不同数组的唯一值。这是示例:

第一条记录

 { Host:"abc.com" ArtId:"123", tags:[ "tag1", "tag2" ] }

第二次记录

{ Host:"abc.com" ArtId:"123", tags:[ "tag2", "tag3" ] }

在主机和 artid 上聚合后,我需要这样的结果:

 { Host: "abc.com", ArtId: "123", count :"2", tags:[ "tag1", "tag2", "tag3" ]}

我在组声明中尝试了$addToset,但它给了我这样的标签:[["tag1","tag2"],["tag2","tag3"]]

您能帮我如何在聚合中实现这一目标

【问题讨论】:

  • @Neil Lunn 我已将其移至此处。如果我在文档中有多个这样的数组,您能否建议我一些在这种情况下应该选择的方法。至于每个数组,我必须按照您的解释重复聚合。但是,如果我在每个数组的第一组中使用 $addToSet,那么我将获得数组数组,我可以在 java 中解析它以为每个字段创建新数组,在这种情况下,我将只为每个所有记录运行一次聚合
  • 我们似乎在这里误会了。您应该已经运行了我在dba.stackexhange.com 上提供的代码,其目的是让问题迁移。它不会产生数组数组。既然你已经这样做了,也会在这里再次发布。您需要查看“双重”$unwind 并实际运行代码。
  • @NeilLunn 抱歉,我并不是说这段代码不能正常工作,它可以完美地工作,它解决了我使用单个数组的问题,但是如果我在文档中有两个或更多数组字段并且所有需要以相同的方式处理。所以在这种情况下,我需要对每个数组字段重复该过程
  • 我认为你误解了这一点。这不是要移动您问自己的问题,而是要问您关于文档中“多个数组”的 new 问题。这是一个问题,而不是对原始问题的补充。你还没问。详细询问您的问题。

标签: mongodb mongodb-query aggregation-framework


【解决方案1】:

TLDR;

现代版本应在初始$group 之后使用$reduce$setUnion,如图所示:

db.collection.aggregate([
  { "$group": {
    "_id": { "Host": "$Host", "ArtId": "$ArtId" },
    "count": { "$sum": 1 },
    "tags": { "$addToSet": "$tags" }
  }},
  { "$addFields": {
    "tags": {
      "$reduce": {
        "input": "$tags",
        "initialValue": [],
        "in": { "$setUnion": [ "$$value", "$$this" ] }
      }
    }
  }}
])

您找到$addToSet 运算符是正确的,但是在处理数组中的内容时,您通常需要先处理$unwind。这将数组条目“反规范化”,并实质上制作父文档的“副本”,其中每个数组条目作为字段中的奇异值。这就是您需要避免在不使用它的情况下看到的行为。

虽然您的“计数”提出了一个有趣的问题,但通过在初始 $group 操作后使用“双重展开”很容易解决:

db.collection.aggregate([
    // Group on the compound key and get the occurrences first
    { "$group": {
        "_id": { "Host": "$Host", "ArtId": "$ArtId" },
        "tcount": { "$sum": 1 },
        "ttags": { "$push": "$tags" }
    }},

    // Unwind twice because "ttags" is now an array of arrays
    { "$unwind": "$ttags" },
    { "$unwind": "$ttags" },

    // Now use $addToSet to get the distinct values        
    { "$group": {
        "_id": "$_id",
        "tcount": { "$first": "$tcount" },
        "tags": { "$addToSet": "$ttags" }
    }},

    // Optionally $project to get the fields out of the _id key
    { "$project": {
        "_id": 0,
        "Host": "$_id.Host",
        "ArtId": "$_id.ArtId",
        "count": "$tcount",
        "tags": "$ttags"
    }}
])

$project 的最后一点也在那里,因为我在聚合管道的其他阶段为每个字段使用了“临时”名称。这是因为在$project 中有一个优化,它可以按照在将任何“新”字段添加到文档“之前”已经出现的顺序从现有阶段“复制”字段。

否则输出将如下所示:

{  "count":2 , "tags":[ "tag1", "tag2", "tag3" ], "Host": "abc.com", "ArtId": "123" }

字段的顺序与您想象的不同。确实微不足道,但对某些人来说很重要,因此值得解释为什么以及如何处理。

所以$unwind 的工作是将项目分开而不是在数组中,并且首先执行$group 可以让您获得“分组”键出现的“计数”。

$first 运算符稍后使用“保留”该“计数”值,因为它只是为“标签”数组中存在的每个值“复制”。无论如何,它都是相同的值,所以没关系。随便挑一个。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-07-26
    • 1970-01-01
    • 2019-06-02
    • 1970-01-01
    • 1970-01-01
    • 2019-05-23
    • 2018-12-24
    相关资源
    最近更新 更多