从 MongoDB 中删除数组中包含相同 属性 值的文档

Removing documents from MongoDB that have same property values contained within an array

我正在尝试从 MongoDB 3.0 数据库中删除共享数组中包含的 2 属性 值 profiles.platformprofiles.handle 的文档。

{
    _id: ID
    profiles: [{
        source: {},
        isProfile: Boolean,
        profile: {},
        demographics: {
            male: Number,
            female: Number
        },
        handle: String,
        platform: String
    }]
}

我尝试使用聚合框架来获取共享这些 属性 值的文档的 _id。

db.collection.aggregate([{
    "$group": {
        "_id": "$id",
        "duplicates": {
            "$addToSet": "$_id"
        },
        "handles": {
            "$addToSet": "$profiles.profile.handle",
            "$addToSet": "$profiles.profile.platform"
        },
        "count": {
            "$sum": 1
        }
    }
}, {
    "$match": {
        "count": {
            "$gt": 1
        }
    }
}, {
    "$out": "dupes"
}])

但这没有用。我收到一个错误

{
    "errmsg": "exception: insert for $out failed: { lastOp: Timestamp 1433113685000|1, connectionId: 4856701, err: \"BSONObj size: 56348873 (0x35BD0C9) is invalid. Size must be between 0 and 16793600(16MB) First element: _id: null\", code: 10334, n: 0, ok: 1.0 }",
    "code": 16996,
    "ok": 0
}

重复文档示例
文档 1

{
    _id: ID
    profiles: [{
        source: {},
        isProfile: true,
        profile: {},
        demographics: {
            male: 1,
            female: 0
        },
        handle:'tom', <--- specific property that is duplicated.
        platform:'myspace' <--- specific property that is duplicated.
    }]
}

文档 2

{
    _id: ID
    profiles: [{
        source: {},
        isProfile: true,
        profile: {},
        demographics: {
            male: 1,
            female: 0
        },
        handle:'tom', <--- specific property that is duplicated.
        platform:'myspace' <--- specific property that is duplicated.
    }]
}

澄清一下: 我需要删除文档 1,因为文档 2 与文档 1 具有相同的值。(删除顺序无关紧要)

删除恰好包含具有特定属性的数组项的文档与具有这些属性的共享数组项的另一个文档相匹配并不是一个简单的操作。

最好结合使用聚合框架和单独的批量写入操作来执行此操作。这基本上是你想要的:

var bulk = db.collection.initializeOrderedBulkOp(),
    count = 0;

db.collection.aggregate([
    // Unwind the array
    { "$unwind": "$profiles" },

    // Group on required keys and count
    { "$group": {
        "_id": {
            "handle": "$profiles.handle",
            "platform": "$profiles.platform"
        },
        "count": { "$sum": 1 }
        "ids": { "$addToSet": "$_id" }
    }},

    // Filter anything that is not a duplicate
    { "$match": { "count": { "$gt": 1 } }},
]).forEach(function(doc) {
    doc.ids.shift();  // remove the first item to keep
    bulk.remove({ "_id": { "$in": doc.ids } });
    count++;

    // Execute only once every 1000 and re-initialize
    if ( count % 1000 == 0 ) {
        bulk.execute();
        bulk = db.collecion.initializeOrderedBulkOp();
    }
});

// Clear any queued
if ( count % 1000 != 0 )
    bulk.execute();

或同等语言。基本上,首先 "identify" 包含重复项的文档,然后 "keeping" 一个来自匹配项的文档或 "excluding" 个来自删除的文档,然后只处理其中一个文档 .remove() "duplicate" 在列表中。

您可以通过另外 "de-duplicating" 文档本身来更进一步,但这可能是一种相当安全的方法,因为它几乎没有额外的开销。

虽然方便,但我个人不会在这里使用 $out 或尝试在聚合管道中完成所有工作。主要是因为 "keeping" 这样一个分组中的整个文档可能会超过 16MB 的 BSON 限制,而且尝试 "flag" 重复条目并且只保留一个条目也会带来很多开销。

如果你有 $out 那么你有一个 MongoDB 也支持批量操作,所以最好使用它们来减少流量。