MongoDB聚合,如何在组管道中addToSet数组的每个元素

Posted

技术标签:

【中文标题】MongoDB聚合,如何在组管道中addToSet数组的每个元素【英文标题】:MongoDB aggregate, how to addToSet each element of array in group pipeline 【发布时间】:2018-04-30 04:22:52 【问题描述】:

我有包含标签字段的文档。这是一个简单的数组,里面有标签名称,里面没有对象也没有 _id。 像["Protocol", "Access", "Leverage", "Capability"]这样的普通标签。

在我的小组管道中,我尝试了'selectedTags': $addToSet: '$tags' 之类的方法,但最终我得到了一个包含标签数组的数组。我对$push 也有同样的看法。

我尝试使用 $each$pushAll,但不支持它们作为我的 shell 告诉我的分组运算符。

有人可以帮我解决这个问题吗?

谢谢

编辑:

示例文档:


    "_id" : "HWEdDGsq86x4ikDSQ",
    "teamId" : "AdLizGnPuqbWNsFHe",
    "ownerId" : "Qb5EigWjqn2t3bfxD",
    "type" : "meeting",
    "topic" : "Grass-roots hybrid knowledge user",
    "fullname" : "Guidouil",
    "startDate" : ISODate("2017-07-30T09:00:05.513Z"),
    "shareResults" : true,
    "open" : true,
    "language" : "fr",
    "tags" : [
        "Protocol",
        "Challenge",
        "Artificial Intelligence",
        "Capability"
    ],
    "isDemo" : true,
    "createdAt" : ISODate("2017-11-15T19:24:05.513Z"),
    "participantsCount" : 10,
    "ratersCount" : 10,
    "averageRating" : 3.4,
    "hasAnswers" : true,
    "updatedAt" : ISODate("2017-11-15T19:24:05.562Z")


    "_id" : "rXvkFndpXwJ6KAvNo",
    "teamId" : "AdLizGnPuqbWNsFHe",
    "ownerId" : "Qb5EigWjqn2t3bfxD",
    "type" : "meeting",
    "topic" : "Profit-focused modular system engine",
    "fullname" : "Guidouil",
    "startDate" : ISODate("2017-07-24T12:00:05.564Z"),
    "shareResults" : true,
    "open" : true,
    "language" : "fr",
    "tags" : [
        "Initiative",
        "Artificial Intelligence",
        "Protocol",
        "Utilisation"
    ],
    "isDemo" : true,
    "createdAt" : ISODate("2017-11-15T19:24:05.564Z"),
    "participantsCount" : 33,
    "ratersCount" : 33,
    "averageRating" : 2.9393939393939394,
    "hasAnswers" : true,
    "updatedAt" : ISODate("2017-11-15T19:24:05.753Z")

聚合:

db.surveys.aggregate(
   $match: query ,
  
    $group: 
      '_id': 
        'year':  $year: '$startDate' ,
        'day':  $dayOfYear: '$startDate' ,
      ,
      'participants':  $sum: '$ratersCount' ,
      'rating':  $avg: '$averageRating' ,
      'surveys':  $push: '$_id' ,
      'selectedTags':  $addToSet: '$tags' ,
      'peoples':  $addToSet: '$fullname' ,
    
  ,
   $sort:  _id: 1  
);

然后我尝试将 selectedTags 更改为 $push: $each: '$tags' $pushAll: '$tags' 但这不会执行:(

编辑 2:

javascript 中我是这样做的:

return Surveys.aggregate(
   $match: query ,
   $group: 
    _id: dateGroup,
    participants:  $sum: '$ratersCount' ,
    rating:  $avg: '$averageRating' ,
    surveys:  $push: '$_id' ,
    selectedTags:  $push: '$tags' ,
    peoples:  $addToSet: '$fullname' ,
   ,
   $project: 
    _id: null,
    selectedTags: 
      $reduce: 
        input: "$selectedTags",
        initialValue: [],
        in:  $setUnion: ["$$value", "$$this"] 
      
    ,
   
);

【问题讨论】:

你能展示你的尝试吗?它可能会对您想要实现的目标有所帮助。 【参考方案1】:

Dannyxu 和 Alex Beck 的回答都有效,但仅在用于小组赛时部分有效。我需要将两者结合起来以获得单个平面标签数组的预期结果:

Model.aggregate()
        .match( /** some query */ )
        .group(
            _id: '$teamId',
            tagsSet:  $push: '$tags' ,
            numRecords:  $sum: 1 ,
        )
        .project(
            _id: 0,
            numRecords: 1,
            tagsSet: 
                $reduce: 
                    input: '$tagsSet',
                    initialValue: [],
                    in:  $setUnion: ['$$value', '$$this'] ,
                ,
            ,
        )
        .unwind( path: '$tagsSet' )
        .group(
            _id: null,
            selectedTags:  $addToSet: '$tagsSet' ,
            numRecords:  $sum: '$numRecords' ,
        )

【讨论】:

【参考方案2】:

您也可以使用$unwind 获取结果:

db.collection.aggregate([
  $unwind: "$tags",
  $group:
     _id: null,
     selectedTags:  $addToSet: '$tags'       
  
])

【讨论】:

谢谢,这个超级简单! 这是一个干净优雅的解决方案。在unwind tags 阶段,内存使用是一个真正的问题。【参考方案3】:

要在聚合管道中模拟$addToSet update operator with $each modifier 的功能,您可以在分组阶段使用$push 和在投影阶段使用$reduce + $setUnion 的组合。例如:

db.collection.aggregate([
    $group:
       _id: null,
       selectedTags:  $push: '$tags'       
    ,
    $project: 
        selectedTags:  $reduce: 
            input: "$selectedTags",
            initialValue: [],
            in: $setUnion : ["$$value", "$$this"]
        
    
])

生成单个文档,其中包含来自selectedTags 数组中所有文档的不同标签列表。

【讨论】:

你说得对,我越来越接近我想要的,现在我有一个包含标签数组的数组。像这样:SelectedTags : [ ["Leverage", "Utilisation", "Capability", "Initiative"] ] 怎么样?答案中的 sn-p 返回一个平面标签数组。 我还需要其他字段,所以我这样做了 嗯,其他字段真的无所谓。只需将小组赛中的selectedTags: $push: '$tags' 替换为我发布的表达式即可。根据需要保留其余字段。 我尝试了这个gist.github.com/guidouil/147774bc36d0d62e1376eedaa1b54ff0,看起来它只是将原始组部分返回给我

以上是关于MongoDB聚合,如何在组管道中addToSet数组的每个元素的主要内容,如果未能解决你的问题,请参考以下文章

排序聚合 addToSet 结果

Spring数据MongoDB无法在组聚合中映射_id

Mongodb聚合管道如何限制组推送

如何在mongodb聚合框架中的管道阶段后加入文档

如何将 Mongodb 聚合管道文档返回到一个文档?

如何使用聚合管道从 mongodb 中的当前字段中减去更新?