javascript - 如何从 MongoDB 数组中删除重复的对象?

标签 javascript mongodb mongodb-query aggregation-framework

我的数据如下所示:

{

    "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_list 是名为 Bar 的模型中的字段。请注意,数组中的第一个和第二个对象是完全重复的。

除了切换到 PostgresSQL 的明显解决方案之外,我还可以运行什么 MongoDB 查询来删除 foo_list 中的重复条目?

不太合适的类似答案:

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

我希望很清楚我对查询数据库不感兴趣;我希望永远从数据库中删除重复项。

最佳答案

纯粹从聚合框架的 Angular 来看,有几种方法可以解决这个问题。

您可以申请 $setUnion在现代版本中:

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

或者更传统的是 $unwind$addToSet :

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" }
    }}
])    

如果您确实想使用另一个更新语句从数据中“删除”重复项,则最后一个表单可能对您有用,因为它标识了重复的元素。

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

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

从您的集合中返回每个文档的结果,其中包含数组中的重复条目以及重复的条目。这是您需要更新的信息,您可以根据需要循环结果,从结果中指定更新信息以删除重复项。

这实际上是通过每个文档 两个 更新语句来完成的,就像一个简单的 $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 及更高版本的方法,使用聚合和批量操作更新的游标结果。但原则保持不变:

  1. 识别文档中的重复项

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

  3. 使用$unsetpositional $运算符将“第一个”匹配的数组元素设置为 null

  4. 使用$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"
            }
    ]
}

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

关于javascript - 如何从 MongoDB 数组中删除重复的对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25417038/

相关文章:

javascript html 高度与窗口高度

mongodb - 将 $strLenCP 与 Spring Data MongoDB 结合使用

c++ - 在 Cygwin 中构建 MongoDB C++ 驱动程序 : generate_error_codes. 找不到 py

mongodb - 如何为 Meteor 集合/minimongo 中的一个文档更新数组中多个对象的属性?

mongodb - 查询数组大小大于 1 的文档

javascript - FullCalendar 时隙高度

javascript - 如何解决 Nightmare .click() 问题不传播?

javascript - 将参数传递给 Node 中的 execFile

node.js - Mongoose 返回空 JSON 数组

regex - 不区分大小写的正则表达式不起作用的 Mongoose 查询 $in