我在MongoDB上有一个非常大的集合,我想从该集合中删除重复的记录。我脑海中浮现的第一个想法是删除索引并使用dropDups重建索引。但是,重复的数据太多,MongoDB无法处理。
所以我转向MapReduce寻求帮助。这是我目前的进展。
m = function () {
emit(this.myid, 1);
}
r = function (k, vals) {
return Array.sum(vals);
}
res = db.userList.mapReduce(m,r, { out : "myoutput" });
所有重复记录的"myid"都存储在"myoutput"集合中。但是,我不知道如何通过引用myoutput.myid从userList中删除记录。它应该是这样的:
db.myoutput.find({value: {$gt: 1}}).forEach(
function(obj) {
db.userList.remove(xxxxxxxxx) // I don't know how to do so
})
顺便说一句,使用 foreach 似乎会用理智的 myid 擦除所有记录。但我只想删除重复记录。前任:
{ "_id" : ObjectId("4edc6773e206a55d1c0000d8"), "myid" : 0 }
{ "_id" : ObjectId("4edc6780e206a55e6100011a"), "myid" : 0 }
{ "_id" : ObjectId("4edc6784e206a55ed30000c1"), "myid" : 0 }
最终结果应仅保留一条记录。有人可以在这方面给我一些帮助吗?
谢谢。:)
最干净的可能是编写删除记录的客户端脚本:
db.myoutput.find({value: {$gt: 1}}).forEach(
function(obj) {
var cur = db.userList.find({ myid: obj._id }, {_id: 1});
var first = true;
while (cur.hasNext()) {
var doc = cur.next();
if (first) {first = false; continue;}
db.userList.remove({ _id: doc._id });
}
})
我还没有测试过这段代码,所以一定要仔细检查是否针对生产数据运行。
虽然上面的答案非常有效,但如果您的数据库/集合中有 900K 或 3M 条记录,它确实非常慢。
如果处理大量数据,我建议走很长的路:
- 使用 GROUP BY 模拟 - db.collection.group(( 选择项目
- 使用reduce函数在数组中存储此数据
- 将导出的数据另存为 JSON
- 使用 mongoimport 再次导入到干净的数据库中。
对于 900K 条目,这大约需要 35 秒(组查询(。
在 PHP 中的实现:
$mongo_client = new MongoClient();
$collection = $mongo_client->selectCollection("main", "settings");
//Group by the field "code"
$keys = array("code" => 1);
//You must create objects for every field you wish to transfer (except the one grouped by - that gets auto-transferred)
$initial = array("location" => "", "name" => "", "score" => 0, "type" => "");
//The reduce function will set the grouped properties
$reduce = "function (obj, prev) { prev.location = obj.location; prev.name = obj.name; prev.score = obj.score; prev.type = obj.type; }";
$fh = fopen("Export.json", "w");
$unique_set = $collection->group($keys, $initial, $reduce);
fwrite($fh, json_encode($unique_set['retval']));
fclose($fh);
如果你的重复项很少,在PHP上运行它可能不是最好的选择,但我的集合有大量的重复项,所以最终的数据集很容易处理。也许有人会发现这对速度很有用。(转移到 MONGO shell 应该相当容易。
但是请记住,您必须重新格式化最终文件,每行有 1 个文档,以便它与 mongoimport 一起使用。(搜索/替换所有内容在这里应该没问题。
/*
* This map reduce will output a new collection: "duplicateinvoices"
* { "_id" : "12345", "value" : 2 }
* { "_id" : "23456", "value" : 2 }
* ...
**/
m = function () {
emit(this.MlsId, 1);
}
r = function (k, vals) {
return Array.sum(vals);
}
res = db.invoices.mapReduce(m,r, { out : "duplicateinvoices" });
/*
* We have two approaches (we should test wich is faster/reliable, i didn't
**/
/* OPTION 1 */
// We iterate over duplicateinvoices and get the media-hash
// of the ones with value > 1 the duplicates
db.duplicateinvoices.find({value: {$gt: 1}}).forEach(
function(invoice) {
// temporary save one of this objects into a variable
var obj = db.invoices.findOne({ media_hash: invoice._id });
// remove all media-hash matched invoices from invoice collection
db.invoices.remove({media_hash: invoice._id})
// insert again the previously saved object into collection
db.invoices.insert(obj)
}
)
/* OPTION 2 */
// We iterate over duplicateinvoices and get the media-hash
// of the ones with value > 1 the duplicates
db.duplicateinvoices.find({value: {$gt: 1}}).forEach(
function(invoice) {
// Invoices cursor with all the media_hash matched documents
var cur = db.invoices.find({ media_hash: invoice._id });
var first = true;
while (cur.hasNext()) {
var doc = cur.next();
// Skip the first one
if (first) {first = false; continue;}
// Delete the others matched documents
db.userList.remove({ _id: doc._id });
}
}
)
来源:
如何通过MapReduce删除MongoDB中的重复记录?http://openmymind.net/2011/1/20/Understanding-Map-Reduce/http://docs.mongodb.org/manual/tutorial/map-reduce-examples/
不需要mapreduce。这个呢: ?在 mongo shell 中粘贴代码:
function removeDupls (collectionName, keyField, reportEvery) {
if (reportEvery === undefined) {reportEvery=10;}
sort = {};
sort[keyField] = 1;
var myidLast;
var res = {docsCnt:0,docsRemoved:0}
db[collectionName].find().sort(sort).clone().forEach(
function(doc) {
res['docsCnt'] += 1;
if (doc.myid == myidLast) {db[collectionName].remove({_id:doc._id}); res['docsRemoved'] +=1;}
else {myidLast = doc.myid;}
if (res['docsCnt'] % reportEvery === 0) {print (JSON.stringify(res))}
}
);
return res;
}
然后称之为:
removeDupls('users','myid',1000)
这将起作用,并且可能比任何MapReduce>删除作业都快(取决于您的重复文档数量(如果你想让它变得非常快,你应该将要删除的文档_ids存储在一个临时数组中,然后使用批量删除。