如何根据多个字段删除 mongodb 中的重复项?

mpc*_*c75 3 mongodb node.js mongodb-query aggregation-framework

这是我的文档的示例:

[{name:"duplicate", value:true, id:2910921},{name:"duplicate", value:true, id:32838293},{name:"duplicate", value:false, id:3283232},{name:"notDuplicate", value:true, id:382932}]
Run Code Online (Sandbox Code Playgroud)

如果有多个文档包含相同的名称相同的值,我想删除。在上面的示例中,它将删除一个文档,或者{name:"duplicate", value:true, id:2910921}{name:"duplicate", value:true, id:32838293},对我来说哪一个并不重要。

到目前为止,我考虑过为每个字段创建一个新字段,类似于 newField: "duplicatetrue",然后我可以在这些字段上使用不同的字段来删除重复项,但我无法弄清楚如何连接两个字段不同类型的不同字段合并到一个新字段中。我也绝对愿意接受更好的建议。这是我到目前为止所拥有的:

db.collection(collectionName).updateMany({}, {$set: {"newField": ["$name","$value"]  }})
Run Code Online (Sandbox Code Playgroud)

但是,上面的行不输出值,而是准确输出 newField: ["$name","$value"]

从 $name 和 $value 中删除引号也不起作用。

我正在使用 Node mongodb 驱动程序:3.5.8

sri*_*asy 6

你可以通过两种方式做到这一点

  1. 在一个数据库调用中:使用聚合运算符$out,也许您也可以使用$merge但这对您的情况没有多大用处。
  2. 在两次数据库调用中:如果您认为$out具有破坏性,并且收集了数百万个文档,那么在生产环境中可能会出现问题,那么您可以首先读取_id要删除的所有文档并使用.deleteMany()删除所有文档立刻。(您可以在文档上使用任何唯一标识符,而不是但_id我已经使用_id它,因为它默认已建立索引 - 这可以帮助运行得deleteMany()更快)。

步骤1:

使用$out- 正如我所说,它是破坏性的,因为如果输入名称匹配,它将覆盖整个集合,或者根据聚合查询的结果创建一个新集合。$out因此,在用作最后阶段之前,请很好地测试您的聚合查询。还将数据写入临时集合并在一切足够好后重命名集合。重命名集合时考虑停机时间

询问 :

db.collection.aggregate([
  {
    $group: { _id: { name: "$name", value: "$value" },
      doc: { $last: "$$ROOT" } // Retrieve only last doc in a group
    }
  },
  {
    $replaceRoot: { newRoot: "$doc" } // replace doc as object as new root of document
  },
  { $out : 'collection_new' } // Test above aggregation & then use this 
])
Run Code Online (Sandbox Code Playgroud)

测试: mongoplayground

第2步:

  1. 使用聚合查询,您将获得_ids要从集合中删除的列表。

询问 :

db.collection.aggregate([
    /**
     * Group on matching docs :
     * { name: "duplicate", value: false}, 
     * { name: "duplicate", value: true}, 
     * { name: "duplicate-yes", value: true},
     * { name: "notDuplicate", value: true} 
     * */
    {
      $group: {
        _id: { name: "$name", value: "$value" },
        _idsNeedsToBeDeleted: { $push: "$$ROOT._id" } // push all `_id`'s to an array
      }
    },
    /** Remove first element - which is removing a doc */
    {
      $project: {
        _id: 0,
        _idsNeedsToBeDeleted: { $slice: [ "$_idsNeedsToBeDeleted", 1, { $size: "$_idsNeedsToBeDeleted" } ] }
      }
    },
    {
      $unwind: "$_idsNeedsToBeDeleted" // Unwind `_idsNeedsToBeDeleted`
    },
    /** Group without a condition & push all `_idsNeedsToBeDeleted` fields to an array */
    {
      $group: { _id: "", _idsNeedsToBeDeleted: { $push: "$_idsNeedsToBeDeleted" } }
    },
    {$project : { _id : 0 }} // Optional stage
     /** At the end you'll have an [{ _idsNeedsToBeDeleted: [_ids] }] or [] */
  ])
Run Code Online (Sandbox Code Playgroud)

测试: mongoplayground

  1. 现在使用.deleteMany()- 删除所有文档:

询问 :

db.collection.deleteMany( { "_id" : {$in : [_ids]} } );
Run Code Online (Sandbox Code Playgroud)

.deleteMany()在您需要检查聚合结果不是空数组[]并且有一个带有_idsNeedsToBeDeleted数组字段的文档之前考虑。_id另外,由于我们在数据库中进行匹配- 聚合_idsNeedsToBeDeleted数组将是一个字符串数组 - 因此迭代数组,将字符串转换为并在删除查询中ObjectId()使用该数组。ObjectId()

笔记 :

无论您选择哪个步骤 - 由于我们正在分组,因此name + value您需要确保您的所有文档都具有这些字段。