MongoDB - 聚合 - 获取数组中的唯一项

24

这是我的 MongoDB 集合:

{
    "_id" : ObjectId("515d8f53175b8ecb053425c2"),
    "category" : "Batteries",
    "products" : [
        {
            "brand" : "Duracell",
            "item" : [
                "AA",
                "AAA"
            ]
        },
        {
            "brand" : "Everyday",
            "item" : [
                "9V",
                "AA",
                "12V"
            ]
        }
    ]
}
我需要的输出是:

1)所有项目的唯一列表

{["AA", "AAA", "9V", "12V"]}

并且每个产品都有独特的物品清单

{
    "category" : "Batteries",
    "item": ["AA", "AAA", "9V", "12V"]
}

我对MongoDB很陌生,尝试了不同聚合函数但似乎都不起作用。请帮忙。

5个回答

34

尝试了几次后,我解决了这个问题。以下是命令:

db.xyz.aggregate( {$project: {a: '$products.item'}}, 
    {$unwind: '$a'}, 
    {$unwind: '$a'}, 
    {$group: {_id: 'a', items: {$addToSet: '$a'}}});

db.xyz.aggregate( {$project: {category: 1, a: '$products.item'}}, 
    {$unwind: '$a'}, 
    {$unwind: '$a'}, 
    {$group: {_id: '$category', items: {$addToSet: '$a'}}});

7
你为什么要“解绕”两次? - Devesh
9
因为“item”嵌套在一个数组中。 - Ananth
如果数组中只有一个项目,您可以使用$first而不是unwind。它返回数组中的第一个项目,并且比$unwind便宜得多。与$addFields结合使用。例如{$addFields:{item:{$first:'products.item'}}} 现在,您可以展开“item”并在“$group”阶段中使用它。 - Emanuel Lindström

9
自mongodb3.4版本起,新增了一个$reduce操作符,因此我们可以在不需要额外阶段的情况下将数组拍平。
col.aggregate([
  {
    $project: {
      items: {
        $reduce: {
          input: "$products.items",
          initialValue: [],
          in: { $concatArrays: ["$$value", "$$this"] },
        },
      },
    },
  },
  { $unwind: "$items" },
  { $group: { _id: null, items: { $addToSet: "$items" } } },
]);

2.

col.aggregate([
  {
    $project: {
      category: 1,
      items: {
        $setUnion: {
          $reduce: {
            input: "$products.items",
            initialValue: [],
            in: { $concatArrays: ["$$value", "$$this"] },
          },
        },
      },
    },
  },
]);

2

我知道这是一个老问题,几年前你已经解决了!但是在你标记为正确答案的回答中存在一个小问题,它可能不适用于所有情况。 $unwind 是一个昂贵的操作符,并且对于大型数据集可能会影响延迟和内存消耗。我认为在这种情况下 $reduce 操作符更加高效。


也许你可以添加一个演示,展示如何使用reduce在这种情况下获取一个唯一的项目集合? - ProgrammingLlama

1

我知道这是一个老问题,但我想展示一个更容易的方法!

setDifference 函数接受两个集合,并返回一个包含 仅存在于第一个集合中的元素 的数组。在执行此操作时,它会忽略重复项。

因此,我通过在第二个变量中使用空数组来欺骗它。

完整代码

db.xyz.aggregate([
{
    $match: { 
        _id: ObjectId("515d8f53175b8ecb053425c2"),
        category: "Batteries"
    }
},
{
    $set: { 
        item: { $setDifference: ["$products.item", []] }
    }
}
])

目前你的回答不够清晰,请编辑并添加更多细节,以帮助其他人理解它如何回答问题。你可以在帮助中心找到有关如何编写好答案的更多信息。 - Community

0

我不确定你在聚合函数中尝试了什么,但我认为展开(unwind)可以帮助你完成相同的任务,假设你无法完成它,我们有一个Map-Reduce可以让你轻松地完成这个任务。你可以查看http://docs.mongodb.org/manual/applications/map-reduce/。它允许你以你想要的方式获取数据,并且你可以轻松地获取列表。我认为在标签列上使用$unwind,然后对它们进行$group操作将始终给出所需的唯一标签列表,就像你在第一种情况下所需要的那样,在第二种情况下创建两个关键字类别和项目的$group,这是之前$unwind的。


Devesh,感谢您的回复。我只需使用聚合就解决了这个问题。我也发布了我的答案。 - Ananth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接