Mongo根据字段删除对象数组中的重复项

Mongo根据字段删除对象数组中的重复项

问题描述:

Mongo的新手,发现了许多使用聚合框架从字符串数组中删除重复对象的示例,但我想知道是否有可能根据对象中的字段从对象数组中删除重复对象.例如

New to Mongo, have found lots of examples of removing dupes from arrays of strings using the aggregation framework, but am wondering if possible to remove dupes from array of objects based on a field in the object. Eg

{
"_id" : ObjectId("5e82661d164941779c2380ca"),
"name" : "something",
"values" : [
    {
        "id" : 1,
        "val" : "x"
    },
    {
        "id" : 1,
        "val" : "x"
    },
    {
        "id" : 2,
        "val" : "y"
    },
    {
        "id" : 1,
        "val" : "xxxxxx"
    }
]
}

在这里,我想根据 id 字段删除重复项.因此会以

Here I'd like to remove dupes based on the id field. So would end up with

{
"_id" : ObjectId("5e82661d164941779c2380ca"),
"name" : "something",
"values" : [
    {
        "id" : 1,
        "val" : "x"
    },
    {
        "id" : 2,
        "val" : "y"
    }
]
}

选择具有给定id的第一个/任何对象都可以.只想以每个id结束一个.这在聚合框架中可行吗?甚至在聚合框架之外,也只是寻找一种干净的方法来做到这一点.需要在集合中的许多文档中执行这种操作,这似乎是聚合框架的一个好用例,但是正如我提到的,这里的新手...谢谢.

Picking the first/any object with given id works. Just want to end up with one per id. Is this doable in aggregation framework? Or even outside aggregation framework, just looking for a clean way to do this. Need to do this type of thing across many documents in collection, which seems like a good use case for aggregation framework, but as I mentioned, newbie here...thanks.

好吧,您可能会以两种方式获得所需的结果.

Well, you may get desired result 2 ways.

展平-删除重复项(首先出现)-分组依据

Flatten - Remove duplicates (pick first occurrence) - Group by

db.collection.aggregate([
  {
    $unwind: "$values"
  },
  {
    $group: {
      _id: "$values.id",
      values: {
        $first: "$values"
      },
      id: {
        $first: "$_id"
      },
      name: {
        $first: "$name"
      }
    }
  },
  {
    $group: {
      _id: "$id",
      name: {
        $first: "$name"
      },
      values: {
        $push: "$values"
      }
    }
  }
])

MongoPlayground

我们需要使用 $ reduce 运算符

伪代码:

values : {
  var tmp = [];
  for (var value in values) {
      if !(value.id in tmp)
        tmp.push(value);
  }
  return tmp;
}


db.collection.aggregate([
  {
    $addFields: {
      values: {
        $reduce: {
          input: "$values",
          initialValue: [],
          in: {
            $concatArrays: [
              "$$value",
              {
                $cond: [
                  {
                    $in: [
                      "$$this.id",
                      "$$value.id"
                    ]
                  },
                  [],
                  [
                    "$$this"
                  ]
                ]
              }
            ]
          }
        }
      }
    }
  }
])

MongoPlayground