如何基于多个字段删除mongodb中的重复项?

问题描述 投票:1回答:3

这是我的文档的一个示例:

[{name:"duplicate", value:true, id:2910921},{name:"duplicate", value:true, id:32838293},{name:"duplicate", value:false, id:3283232},{name:"notDuplicate", value:true, id:382932}]

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

到目前为止,我已经考虑只为每个字段创建一个新字段,就像newField:“ duplicatetrue”,然后我可以在其中使用distinct来删除重复项,但是我在弄清楚如何将具有不同类型的两个不同字段合并为一个新字段。我当然也愿意接受更好的建议。这是我到目前为止的内容:

db.collection(collectionName).updateMany({}, {$set: {"newField": ["$name","$value"]  }})

但是,上面的行不输出值,而是输出完全为newField的值:[“ $ name”,“ $ value”]

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

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

node.js mongodb mongodb-query aggregation-framework
3个回答
1
投票

您可以通过两种方式完成

  1. 在一个数据库调用中:使用聚合运算符$out,也许您也可以使用$merge,但这对您的情况没有多大用处。
  2. 在两个数据库调用中:好像您认为$out具有破坏性,并且在生产环境中收集数百万个文档可能是一个问题,那么您可以首先读取所有_id的文档以删除并使用.deleteMany()一次删除所有文档。 (您可以在文档上使用任何唯一标识符而不是_id,但我默认使用_id作为它的索引-这可以帮助更快地运行deleteMany()。)>
  3. 步骤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 
])

Test:

mongoplayground

步骤2:

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

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 [] */
  ])

Test:

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

db.collection.deleteMany( { "_id" : {$in : [_ids]} } );

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

注意:

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


0
投票

我不确定mongo,但是您可以使用该节点删除重复项。我尝试这种方法的要求之一,可以正常工作。请在需要var名称时通过尝试尝试此操作。


0
投票

在节点中本地执行似乎很有效(可能不是最快或最有效的方法,但以下方法可行:]

© www.soinside.com 2019 - 2024. All rights reserved.