如何在Mongo聚合中合并文档中的数组字段

vir*_*ren 11 mongodb mongodb-query aggregation-framework

我有一个要求,我需要在两个记录上进行聚合,两个记录都有一个具有不同值的数组字段.我需要的是当我对这些记录进行聚合时,结果应该有一个数组,其中包含来自不同数组的唯一值.这是一个例子:

第一记录

 { Host:"abc.com" ArtId:"123", tags:[ "tag1", "tag2" ] }
Run Code Online (Sandbox Code Playgroud)

第二个记录

{ Host:"abc.com" ArtId:"123", tags:[ "tag2", "tag3" ] }
Run Code Online (Sandbox Code Playgroud)

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

 { Host: "abc.com", ArtId: "123", count :"2", tags:[ "tag1", "tag2", "tag3" ]}
Run Code Online (Sandbox Code Playgroud)

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

你能帮我解决一下如何在聚合中实现这个目标吗?

Nei*_*unn 26

TLDR;

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

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" ] }
      }
    }
  }}
])
Run Code Online (Sandbox Code Playgroud)

你找对$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"
    }}
])
Run Code Online (Sandbox Code Playgroud)

最后一点$project也是因为我在聚合管道的其他阶段为每个字段使用了"临时"名称.这是因为存在一种优化,$project即根据它们在"任何"新"字段被添加到文档之前已经出现的顺序"复制"现有阶段的字段.

否则输出看起来像:

{  "count":2 , "tags":[ "tag1", "tag2", "tag3" ], "Host": "abc.com", "ArtId": "123" }
Run Code Online (Sandbox Code Playgroud)

字段的顺序与您想象的不同.真的很琐碎,但对某些人来说很重要,所以值得解释为什么以及如何处理.

那么$unwind保持项目分离而不是数组的工作也是如此,并且$group首先允许您获得"分组"键出现的"计数".

$first稍后使用的操作符"保持"该"计数"值,因为它对于"标签"数组中存在的每个值都"重复".无论如何它都是相同的价值,所以无所谓.选择一个.