当前位置: 首页 > 知识库问答 >
问题:

如何从MongoDB阵列中删除重复对象?

伊裕
2023-03-14

我的数据如下所示:

{

    "foo_list": [
      {
        "id": "98aa4987-d812-4aba-ac20-92d1079f87b2",
        "name": "Foo 1",
        "slug": "foo-1"
      },
      {
        "id": "98aa4987-d812-4aba-ac20-92d1079f87b2",
        "name": "Foo 1",
        "slug": "foo-1"
      },
      {
        "id": "157569ec-abab-4bfb-b732-55e9c8f4a57d",
        "name": "Foo 3",
        "slug": "foo-3"
      }
    ]
}

其中,foo\u list是一个名为Bar的模型中的字段。请注意,阵列中的第一个和第二个对象是完全重复的。

除了切换到PostgresSQL这一显而易见的解决方案之外,我还可以运行什么MongoDB查询来删除foo\u列表中的重复条目?

类似的答案并不能完全解决这个问题:

  • https://stackoverflow.com/a/16907596/432
  • https://stackoverflow.com/a/18804460/432

这些问题回答了数组中是否有裸字符串的问题。然而,在我的情况下,数组充满了对象。

我希望很明显,我对查询数据库不感兴趣;我希望数据库中的副本永远消失。

共有1个答案

夹谷硕
2023-03-14

纯粹从聚合框架的角度来看,有几种方法可以做到这一点。

您可以在现代版本中直接应用$setUnion:

 db.collection.aggregate([
     { "$project": { 
         "foo_list": { "$setUnion": [ "$foo_list", "$foo_list" ] }
     }}
 ])

或者更传统地使用$展开和$添加设置:

db.collection.aggregate([
    { "$unwind": "$foo_list" },
    { "$group": {
        "_id": "$_id",
        "foo_list": { "$addToSet": "$foo_list" }
    }}
])

或者,如果您只对副本感兴趣,则按常规分组:

db.collection.aggregate([
    { "$unwind": "$foo_list" },
    { "$group": {
        "_id": {
            "_id": "$_id",
            "foo_list": "$foo_list"
        },
        "count": { "$sum": 1 }
    }},
    { "$match": { "count": { "$ne": 1 } } },
    { "$group": {
        "_id": "$_id._id",
        "foo_list": { "$push": "$_id.foo_list" }
    }}
])    

如果您确实想用另一条update语句“删除”数据中的重复项,那么最后一个表单可能会很有用,因为它标识了重复的元素。

因此,在最后一种形式中,示例数据返回的结果标识了重复项:

{
    "_id" : ObjectId("53f5f7314ffa9b02cf01c076"),
    "foo_list" : [
            {
                    "id" : "98aa4987-d812-4aba-ac20-92d1079f87b2",
                    "name" : "Foo 1",
                    "slug" : "foo-1"
            }
    ]
}

其中,对于数组中包含重复条目的每个文档,从集合中返回结果,并且哪些条目是重复的。这是需要更新的信息,当需要从结果中指定更新信息以删除重复项时,可以循环结果。

这实际上是通过每个文档使用两个update语句来完成的,因为一个简单的$pull操作将删除“这两个”项,这不是您想要的:

var cursor = db.collection.aggregate([
    { "$unwind": "$foo_list" },
    { "$group": {
        "_id": {
            "_id": "$_id",
            "foo_list": "$foo_list"
        },
        "count": { "$sum": 1 }
    }},
    { "$match": { "count": { "$ne": 1 } } },
    { "$group": {
        "_id": "$_id._id",
        "foo_list": { "$push": "$_id.foo_list" }
    }}
])    

var batch = db.collection.initializeOrderedBulkOp();
var count = 0;

cursor.forEach(function(doc) {
    doc.foo_list.forEach(function(dup) {
        batch.find({ "_id": doc._id, "foo_list": { "$elemMatch": dup } }).updateOne({
            "$unset": { "foo_list.$": "" }
        });
        batch.find({ "_id": doc._id }).updateOne({ 
            "$pull": { "foo_list": null }
        });
    ]);

    count++;
    if ( count % 500 == 0 ) {
        batch.execute();
        batch = db.collection.initializeOrderedBulkOp();
    }
});

if ( count % 500 != 0 )
    batch.execute();

这是现代MongoDB 2.6及更高版本的实现方式,通过聚合和批量更新操作生成光标。但原则不变:

>

  • 识别文档中的重复项

    循环结果以发布对受影响文档的更新

    使用位置运算符unset将“第一个”匹配的数组元素设置为null

    使用$pull从数组中删除null项

    因此,在处理了上述操作之后,您的示例现在如下所示:

    {
        "_id" : ObjectId("53f5f7314ffa9b02cf01c076"),
        "foo_list" : [
                {
                        "id" : "98aa4987-d812-4aba-ac20-92d1079f87b2",
                        "name" : "Foo 1",
                        "slug" : "foo-1"
                },
                {
                        "id" : "157569ec-abab-4bfb-b732-55e9c8f4a57d",
                        "name" : "Foo 3",
                        "slug" : "foo-3"
                }
        ]
    }
    

    删除重复项时“重复”项仍然完好无损。这就是您从集合中识别和删除重复数据的过程。

  •  类似资料:
    • 我有以下id为唯一键的对象数组”: 由此我想使用扩展运算符检索唯一对象,我尝试使用以下代码: 我只能检索id,如何使用扩散算子检索唯一对象。此外,任何新的ES6功能实现都会有所帮助。

    • 本文向大家介绍如何从MongoDB集合中删除重复项,包括了如何从MongoDB集合中删除重复项的使用技巧和注意事项,需要的朋友参考一下 为此,设置“ unique:true ”,即唯一约束,并避免插入重复项,如以下语法所示: 为了理解上述语法,让我们创建一个包含文档的集合。在这里,不允许重复插入- 在find()方法的帮助下显示集合中的所有文档- 这将产生以下输出-

    • 问题内容: 假设我们有一个具有这种格式的对象: 我想做一个通过键删除的函数: 问题答案: 该运算符允许您从对象中删除属性。 以下示例都做同样的事情。

    • 我有一个arraylist,它包含一对整数(例如int i,int j)。但它可能包含重复对(如(int i,int j)和(int j,int i))。现在如何在O(n)时间复杂度中去除重复。

    • 问题内容: 我想从列表中删除重复项,但我无法正常工作: 问题答案: 如果该代码不起作用,则可能是你未在该类上正确实现。 大概有一些钥匙(我们称之为)可以唯一地标识一个客户。例如 的适当定义equals(Object)如下所示: 为了完整起见,你还应该实现hashCode两个Customer相等的对象将返回相同的哈希值。hashCode上述定义的匹配项为equals: 还值得注意的是,如果列表很大,

    • 我正在使用MongoDB聚合框架展开一个数组,该数组有重复项,我需要在进一步分组时忽略这些重复项。 我如何才能做到这一点?