根据 Mongodb 中的键和引用对象删除重复项?

Remove duplicates based on a key and referenced Objects in Mongodb?

我有 Mongo演员和电影的数据库模型。 Mongo两种模型的结构如下:

var ActorsSchema = new Schema({
    id : {
        type : Number
    },
    known_for:[{
        type: Schema.Types.ObjectId,
        ref: 'Movie'
    }]
})

var MovieSchema = new Schema({
    genres: [{
        type: Schema.Types.ObjectId,
        ref: 'Genre'
    }],
    id: {
        type: Number
    }
});

known_for 演员模型中的属性包含对该演员主演的电影列表的引用。

我想删除重复的 Actor 记录,这些记录将使用 id 字段(而不是 _id)来确定。但我还想做的是删除 known_for 字段中已删除演员记录中引用的电影也被删除,我想从 Mongo 界面中删除这些电影中的记录数文档非常大,以编程方式执行此功能时间效率低下。

我查看了相关的 question,但它不适用于引用其他模型作为字段的模型。

考虑使用聚合框架来识别重复文档,获取演员集合的重复列表 _ids 以及电影 ID 数组,并使用 ids 数组作为查询发出删除和更新命令.

出于测试目的,假设您的集合中有以下数据(具有最少的测试用例,当然是为了演示目的):

db.movies.insert([
    {
        "_id" : ObjectId("5543e79e42063d2be5d2ea84"),
        "id" : 1,
        "genres" : []
    },
    {
        "_id" : ObjectId("5543e79e42063d2be5d2ea85"),
        "id" : 2,
        "genres" : []
    },
    {
        "_id" : ObjectId("5543e79e42063d2be5d2ea86"),
        "id" : 3,
        "genres" : []
    }
]);

db.actors.insert([
    { id: 1, known_for: [ObjectId("5543e79e42063d2be5d2ea84")] },
    { id: 1, known_for: [ObjectId("5543e79e42063d2be5d2ea84")] },
    { id: 2, known_for: [ObjectId("5543e79e42063d2be5d2ea84"), ObjectId("5543e79e42063d2be5d2ea85")] },
    { id: 3, known_for: [ObjectId("5543e79e42063d2be5d2ea85"), ObjectId("5543e79e42063d2be5d2ea86")] }
]);

现在是神奇的部分。聚合管道按 id 对演员文档进行分组,计算分组计数,创建两个数组字段,其中包含演员 _id 副本和电影对象 ID。管道将结果输出到集合 dupes,稍后将使用它来删除重复项:

db.actors.aggregate([
    {
        "$group": {
            "_id": "$id",
            "duplicates": { "$addToSet": "$_id" },
            "movies": { "$addToSet": "$known_for"},
            "count": { "$sum": 1 }
        }
    },
    {
        "$match": {
            "count": { "$gt": 1 }
        }
    },
    {
        "$out": "dupes"
    }
])

查询dupes集合会得到结果:

/* 1 */
{
    "_id" : 1.0000000000000000,
    "duplicates" : [ 
        ObjectId("5543fc8e42063d2be5d2eaa2"), 
        ObjectId("5543fc8e42063d2be5d2eaa1")
    ],
    "movies" : [ 
        [ 
            ObjectId("5543e79e42063d2be5d2ea84")
        ]
    ],
    "count" : 2
}

现在是有趣的部分。然后使用 dupes 集合从 actors 集合中删除 dupes。正如您从 dupes 集合中注意到的那样,movies 字段是一个数组数组,因此您需要将其展平并使用展平的数组来删除电影并提取孤立的电影引用来自演员合集:

db.dupes.find({}).find({}).forEach( function (doc) {
    var movie_dupes = [];    
    db.actors.remove({ "_id": { "$in": doc.duplicates } });    

    doc.movies.forEach( function (arr){
        arr.forEach(function (id){
            movie_dupes.push(id)
        });    
    });
    db.movies.remove({ "_id": { "$in": movie_dupes } });
    db.actors.update({ "known_for": { "$in": movie_dupes } }, { "$pull": { "known_for": { "$in": movie_dupes } } }, { "multi": true });    

});

控制台日志:

Removed 2 record(s) in 38ms
Removed 1 record(s) in 2ms
Updated 1 existing record(s) in 1ms

现在验证我们的重复项是否已被删除:

db.actors.find()

/* 1 */
{
    "_id" : ObjectId("5543fc8e42063d2be5d2eaa3"),
    "id" : 2,
    "known_for" : [ 
        ObjectId("5543e79e42063d2be5d2ea85")
    ]
}

/* 2 */
{
    "_id" : ObjectId("5543fc8e42063d2be5d2eaa4"),
    "id" : 3,
    "known_for" : [ 
        ObjectId("5543e79e42063d2be5d2ea85"), 
        ObjectId("5543e79e42063d2be5d2ea86")
    ]
}

具有 id 1(重复)的演员确实被删除了。

db.movies.find()

/* 1 */
{
    "_id" : ObjectId("5543e79e42063d2be5d2ea85"),
    "id" : 2,
    "genres" : []
}

/* 2 */
{
    "_id" : ObjectId("5543e79e42063d2be5d2ea86"),
    "id" : 3,
    "genres" : []
}

带有 ObjectId("5543e79e42063d2be5d2ea84") 的电影已被删除。