2012-02-29 39 views
46

Giả sử tôi có một bộ sưu tập với một số bộ tài liệu. một cái gì đó như thế này.Tìm tất cả các tài liệu trùng lặp trong bộ sưu tập MongoDB bởi một trường khóa

{ "_id" : ObjectId("4f127fa55e7242718200002d"), "id":1, "name" : "foo"} 
{ "_id" : ObjectId("4f127fa55e7242718200002d"), "id":2, "name" : "bar"} 
{ "_id" : ObjectId("4f127fa55e7242718200002d"), "id":3, "name" : "baz"} 
{ "_id" : ObjectId("4f127fa55e7242718200002d"), "id":4, "name" : "foo"} 
{ "_id" : ObjectId("4f127fa55e7242718200002d"), "id":5, "name" : "bar"} 
{ "_id" : ObjectId("4f127fa55e7242718200002d"), "id":6, "name" : "bar"} 

Tôi muốn tìm tất cả các mục nhập trùng lặp trong bộ sưu tập này theo trường "tên". Ví dụ. "foo" xuất hiện hai lần và "thanh" xuất hiện 3 lần.

+0

cho loại bỏ bản sao bạn có thể sử dụng [giải pháp này] (http: // stackoverflow.com/a/33364353/1045444) –

Trả lời

16

Lưu ý: giải pháp này là dễ hiểu nhất, nhưng không phải là tốt nhất.

Bạn có thể sử dụng mapReduce để tìm hiểu bao nhiêu lần một tài liệu có chứa một lĩnh vực nhất định:

var map = function(){ 
    if(this.name) { 
     emit(this.name, 1); 
    } 
} 

var reduce = function(key, values){ 
    return Array.sum(values); 
} 

var res = db.collection.mapReduce(map, reduce, {out:{ inline : 1}}); 
db[res.result].find({value: {$gt: 1}}).sort({value: -1}); 
5

Đối với một giải pháp Mongo generic, xem MongoDB cookbook recipe for finding duplicates using group. Lưu ý rằng tập hợp nhanh hơn và mạnh hơn ở chỗ nó có thể trả về các bản ghi trùng lặp của _id.

Đối với , câu trả lời được chấp nhận (sử dụng mapReduce) không hiệu quả. Thay vào đó, chúng ta có thể sử dụng phương pháp group:

$connection = 'mongodb://localhost:27017'; 
$con  = new Mongo($connection); // mongo db connection 

$db   = $con->test; // database 
$collection = $db->prb; // table 

$keys  = array("name" => 1); Select name field, group by it 

// set intial values 
$initial = array("count" => 0); 

// JavaScript function to perform 
$reduce  = "function (obj, prev) { prev.count++; }"; 

$g   = $collection->group($keys, $initial, $reduce); 

echo "<pre>"; 
print_r($g); 

Output sẽ là thế này:

Array 
(
    [retval] => Array 
     (
      [0] => Array 
       (
        [name] => 
        [count] => 1 
       ) 

      [1] => Array 
       (
        [name] => MongoDB 
        [count] => 2 
       ) 

     ) 

    [count] => 3 
    [keys] => 2 
    [ok] => 1 
) 

Truy vấn SQL tương đương sẽ là: SELECT name, COUNT(name) FROM prb GROUP BY name. Lưu ý rằng chúng ta vẫn cần phải lọc ra các phần tử có tổng số là 0 từ mảng. Một lần nữa, hãy tham khảo các MongoDB cookbook recipe for finding duplicates using group cho các giải pháp kinh điển sử dụng group.

+0

Liên kết tới sách dạy nấu ăn MongoDB đã lỗi thời và trả về 404. – udachny

131

Câu trả lời được chấp nhận là quá chậm đối với các bộ sưu tập lớn và không trả lại các bản ghi trùng lặp của _id.

Aggregation là nhanh hơn nhiều và có thể trả lại _id s:

db.collection.aggregate([ 
    { $group: { 
    _id: { name: "$name" }, // replace `name` here twice 
    uniqueIds: { $addToSet: "$_id" }, 
    count: { $sum: 1 } 
    } }, 
    { $match: { 
    count: { $gte: 2 } 
    } }, 
    { $sort : { count : -1} }, 
    { $limit : 10 } 
]); 

Trong giai đoạn đầu tiên của đường ống dẫn tập hợp, các nhà điều hành $group tập hợp tài liệu theo lĩnh vực name và các cửa hàng trong uniqueIds mỗi _id giá trị của các bản ghi được nhóm. Toán tử $sum cộng các giá trị của các trường được truyền cho nó, trong trường hợp này là hằng số 1 - do đó đếm số lượng các bản ghi được nhóm vào trường count.

Trong giai đoạn thứ hai của đường ống, chúng tôi sử dụng $match để lọc tài liệu có số ít nhất 2, tức là các bản sao.

Sau đó, chúng tôi sắp xếp các bản sao thường xuyên nhất đầu tiên, và giới hạn kết quả top 10.

Truy vấn này sản lượng lên đến $limit hồ sơ với tên trùng lặp, cùng với _id s của họ sẽ. Ví dụ:

{ 
    "_id" : { 
    "name" : "Toothpick" 
}, 
    "uniqueIds" : [ 
    "xzuzJd2qatfJCSvkN", 
    "9bpewBsKbrGBQexv4", 
    "fi3Gscg9M64BQdArv", 
    ], 
    "count" : 3 
}, 
{ 
    "_id" : { 
    "name" : "Broom" 
    }, 
    "uniqueIds" : [ 
    "3vwny3YEj2qBsmmhA", 
    "gJeWGcuX6Wk69oFYD" 
    ], 
    "count" : 2 
} 
+0

Để xóa các bản sao, bạn có thể sử dụng [giải pháp này] (http://stackoverflow.com/a/33364353/1045444) –

+0

Làm cách nào để có thể Tôi gọi điều này bằng C#? – blueprintChris

+0

Giải pháp này có sử dụng chỉ mục hiện có trên khóa không? Mối quan tâm của tôi đang chạy điều này đối với các bộ sưu tập rất lớn, nơi các tài liệu được nhóm lại có thể không phù hợp với bộ nhớ. – Iravanchi

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