如何在mongodb中删除具有特定条件的重复项?

时间:2015-10-01 18:28:32

标签: mongodb mongodb-query aggregation-framework

例如,我的收藏中有以下文件:

{
    "_id" : "GuqXmAkkARqhBDqhy",
    "beatmapset_id" : "342537",
    "version" : "MX",
    "diff_approach" : "5",
    "artist" : "Yousei Teikoku",
    "title" : "Kokou no Sousei",
    "difficultyrating" : "3.5552737712860107"
}
{
    "_id" : "oHLT7KqsB7bztBGvu",
    "beatmapset_id" : "342537",
    "version" : "HD",
    "diff_approach" : "5",
    "artist" : "Yousei Teikoku",
    "title" : "Kokou no Sousei",
    "difficultyrating" : "2.7515676021575928"
}
{
    "_id" : "GbotZfrPEwW69FkGD",
    "beatmapset_id" : "342537",
    "version" : "NM",
    "diff_approach" : "5",
    "artist" : "Yousei Teikoku",
    "title" : "Kokou no Sousei",
    "difficultyrating" : "0"
}

这些文件具有相同的密钥beatmapset_id 我想删除所有重复项,但保留最多difficultyrating的文档。

我尝试了db.collection.ensureIndex({beatmapset_id: 1}, {unique: true, dropDups: true}),但它留下了一份随机文档,我想要上述条件。

我该怎么做?

2 个答案:

答案 0 :(得分:2)

首先,您需要更新文档并将difficultyratingbeatmapset_id更改为浮点数。为此,您需要使用.forEach方法遍历每个文档,并使用"Bulk"操作更新每个文档,以实现最高效率。

var bulk = db.collection.initializeOrderedBulkOp();
var count = 0;
db.collection.find().forEach(function(doc) { 
    bulk.find({ '_id': doc._id }).update({ 
        '$set': { 
            'beatmapset_id': parseFloat(doc.beatmapset_id), 
            'difficultyrating': parseFloat(doc.difficultyrating) 
        } 
    });
    count++; 
    if(count % 100 == 0) {     
        bulk.execute();     
        bulk = db.collection.initializeOrderedBulkOp(); 
    } 
})

if(count > 0) { 
    bulk.execute(); 
}

现在和#34; dropDups"索引创建的语法已被弃用"从MongoDB 2.6开始,在MongoDB 3.0中删除。这是你删除重复的方法。

这里的主要思想是首先按difficultyrating按降序对文档进行排序。

bulk  = db.collection.initializeUnorderedBulkOp();
count = 0;
db.collection.aggregate([
    { '$sort': { 'difficultyrating': -1 }}, 
    { '$group': { '_id': '$beatmapset_id', 'ids': { '$push': '$_id' }, 'count': { '$sum': 1 }}}, 
    { '$match': { 'count': { '$gt': 1 }}}
]).forEach(function(doc) {
    doc.ids.shift();
    bulk.find({'_id': { '$in': doc.ids }}).remove(); 
    count++; 
    if(count === 100) { 
        bulk.execute(); 
        bulk = db.collection.initializeUnorderedBulkOp();
    }
})

if(count !== 0) { 
    bulk.execute(); 
}

answer涵盖了主题以获取更多详细信息。

答案 1 :(得分:1)

您可以采取的一种方法是通过聚合框架获取具有重复 beatmapset_id 的文档的唯一ID列表:

db.collection.aggregate([
    {
        "$group": {
            "_id": "$beatmapset_id",
            "count": { "$sum": 1 },
            "uniqueIds": { "$addToSet": "$_id" },
            "maxRating": { "$max": "$difficultyrating" }
        }
    },
    { 
        "$match": { 
            "count": { "$gte": 2 } 
        } 
    },
    { 
        "$sort" : { "count" : -1 } 
    }
]);

在此示例管道的第一阶段,我们使用 $group 运算符按所需的索引键值聚合文档,并记录(在uniqueIds字段中)每个{{1}分组文档的值。我们还使用 $sum 运算符计算分组文档的数量,该运算符将传递给它的字段的值相加,在本例中为常量1 - 从而计算分组记录的数量进入计数领域。我们还使用 $max 运算符获得该组的最大_id值。

在此示例管道的第二阶段,我们使用 $match 运算符过滤掉所有计数为1的文档。过滤掉的文档代表唯一索引键。< / p>

其余文档标识集合中包含重复键的文档。

示例输出:

difficultyrating

由于 db.collection.aggregate() 方法返回游标并且可以返回任意大小的结果集,因此请使用 cursor 方法 {{ 3}} 迭代光标并访问结果文档,然后使用 forEach() Bulk API 操作简化:

/* 0 */
{
    "result" : [ 
        {
            "_id" : "342537",
            "count" : 3,
            "uniqueIds" : [ 
                "GbotZfrPEwW69FkGD", 
                "oHLT7KqsB7bztBGvu", 
                "GuqXmAkkARqhBDqhy"
            ],
            "maxRating" : "3.5552737712860107"
        }
    ],
    "ok" : 1
}