2011-12-06 48 views
5

Tôi có một bộ sưu tập rất lớn trên MongoDB và tôi muốn xóa bản ghi trùng lặp khỏi bộ sưu tập đó. Ý nghĩ đầu tiên đến với tâm trí của tôi là thả chỉ mục và xây dựng lại chỉ mục bằng dropDups. Tuy nhiên, dữ liệu trùng lặp là quá nhiều để được xử lý bởi MongoDB.Làm thế nào để loại bỏ bản ghi trùng lặp trong MongoDB bởi MapReduce?

Vì vậy, tôi chuyển sang MapReduce để được trợ giúp. Đây là tiến bộ hiện tại của tôi.

m = function() { 
    emit(this.myid, 1); 
} 

r = function (k, vals) { 
    return Array.sum(vals); 
} 

res = db.userList.mapReduce(m,r, { out : "myoutput" }); 

Và tất cả bản ghi "myid" của bản ghi trùng lặp được lưu trữ trong bộ sưu tập "myoutput". Tuy nhiên, tôi không biết cách xóa bản ghi khỏi userList bằng cách tham chiếu myoutput.myid. Nó cho là một cái gì đó như thế này:

db.myoutput.find({value: {$gt: 1}}).forEach(
    function(obj) { 
     db.userList.remove(xxxxxxxxx) // I don't know how to do so 
}) 

Btw, sử dụng foreach dường như sẽ xóa tất cả các bản ghi với myid lành mạnh. Nhưng tôi chỉ muốn xóa các bản ghi trùng lặp. Ví dụ:

{ "_id" : ObjectId("4edc6773e206a55d1c0000d8"), "myid" : 0 } 
{ "_id" : ObjectId("4edc6780e206a55e6100011a"), "myid" : 0 } 

{ "_id" : ObjectId("4edc6784e206a55ed30000c1"), "myid" : 0 } 

Kết quả cuối cùng sẽ chỉ giữ lại một bản ghi. Ai đó có thể cho tôi một số trợ giúp về điều này?

Cảm ơn bạn. :)

Trả lời

8

sạch nhất có lẽ là để viết một kịch bản client-side mà xóa hồ sơ:

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 }); 
    } 
}) 

tôi đã không kiểm tra mã này để luôn luôn kiểm tra nếu chạy với dữ liệu sản ..

+3

Cảm ơn bạn. Nó hoạt động. Tuy nhiên, sẽ có bản ghi 3M trong bộ sưu tập của tôi. Tốc độ thực thi cực kỳ chậm. Có thể tăng tốc nó không? –

1

Trong khi câu trả lời ở trên là khá hiệu quả, nó thực sự là rất chậm nếu bạn có 900K hoặc 3M hồ sơ trong cơ sở dữ liệu của bạn/bộ sưu tập.

Nếu đối phó với một lượng lớn dữ liệu, tôi khuyên con đường dài:

  • Chọn các mục bằng cách sử dụng GROUP BY analog - db.collection.group()
  • Lưu trữ dữ liệu này bằng cách sử dụng chức năng giảm trong một mảng
  • Lưu dữ liệu đã xuất dưới dạng JSON
  • Nhập lại bằng cách sử dụng mongoimport vào cơ sở dữ liệu sạch.

Đối với các mục nhập 900K, điều này mất khoảng 35 giây (truy vấn nhóm).

thực hiện trong 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); 

Nếu bạn có rất ít bản sao, chạy nó trên PHP thể không phải là lựa chọn tốt nhất, nhưng bộ của tôi đã có một số lượng lớn các bản sao, vì vậy số liệu cuối cùng là dễ dàng để xử lý. Có lẽ ai đó sẽ thấy điều này hữu ích cho tốc độ. (và chuyển sang vỏ mongo khá dễ dàng.)

Hãy nhớ rằng, tuy nhiên, bạn sẽ phải định dạng lại tệp cuối cùng để có 1 tài liệu trên mỗi dòng để nó hoạt động với mongoimport. (Tìm kiếm/thay thế tất cả sẽ ổn ở đây.)

0
/* 
* 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 }); 
     } 
    } 
) 

Nguồn:

How to remove duplicate record in MongoDB by MapReduce? http://openmymind.net/2011/1/20/Understanding-Map-Reduce/ http://docs.mongodb.org/manual/tutorial/map-reduce-examples/

1

thực sự không có nhu cầu về MapReduce đây. Điều gì về điều này:? dán mã trong vỏ Mongo:

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; 
} 

sau đó gọi nó là:

removeDupls('users','myid',1000) 

này sẽ làm việc và có lẽ nó sẽ nhanh hơn so với bất kỳ MapReduce> loại bỏ công việc (tùy thuộc vào số lượng của bạn tài liệu trùng lặp) Nếu bạn muốn làm cho nó thực sự nhanh chóng, bạn nên lưu trữ _ids của tài liệu được loại bỏ trong một mảng tạm thời sau đó sử dụng loại bỏ hàng loạt.

Các vấn đề liên quan