2010-11-19 2 views
18

Tôi có một bộ sưu tập md5 ở mongodb. Tôi muốn tìm tất cả các bản sao. Cột md5 được lập chỉ mục. Bạn có biết cách nhanh chóng để làm điều đó bằng cách sử dụng bản đồ giảm hay không. Hoặc tôi có nên lặp lại tất cả các bản ghi và kiểm tra các bản sao theo cách thủ công không?Cách nhanh chóng để tìm các bản sao trên cột được lập chỉ mục trong mongodb

cách tiếp cận hiện tại của tôi sử dụng MapReduce lặp trên bộ sưu tập gần gấp đôi (giả sử rằng có số lượng rất nhỏ các bản sao):

res = db.files.mapReduce(
    function() { 
     emit(this.md5, 1); 
    }, 
    function (key, vals) { 
     return Array.sum(vals); 
    } 
) 

db[res.result].find({value: {$gte:1}}).forEach(
function (obj) { 
    out.duplicates.insert(obj) 
}); 

Trả lời

30

Cách dễ nhất để thực hiện trong một lần là sắp xếp theo md5 và sau đó xử lý một cách thích hợp.

Cái gì như:

var previous_md5; 
db.files.find({"md5" : {$exists:true} }, {"md5" : 1}).sort({ "md5" : 1}).forEach(function(current) { 

    if(current.md5 == previous_md5){ 
    db.duplicates.update({"_id" : current.md5}, { "$inc" : {count:1} }, true); 
    } 

    previous_md5 = current.md5; 

}); 

Đó chút kịch bản sắp xếp các mục md5 và vòng qua chúng theo thứ tự. Nếu một md5 được lặp lại, thì chúng sẽ là "back-to-back" sau khi sắp xếp. Vì vậy, chúng tôi chỉ cần giữ một con trỏ đến previous_md5 và so sánh nó current.md5. Nếu chúng tôi tìm thấy một bản sao, tôi sẽ bỏ nó vào bộ sưu tập duplicates (và sử dụng $ inc để đếm số lượng bản sao).

Tập lệnh này có nghĩa là bạn chỉ phải lặp qua tập dữ liệu chính một lần. Sau đó, bạn có thể lặp qua bộ sưu tập duplicates và thực hiện dọn dẹp.

5

Bạn có thể làm một nhóm bởi lĩnh vực đó và sau đó truy vấn để có được nhân đôi (có số> 1). http://www.mongodb.org/display/DOCS/Aggregation#Aggregation-Group

Mặc dù, điều nhanh nhất có thể là chỉ cần thực hiện truy vấn chỉ trả về trường đó và sau đó thực hiện tổng hợp trong ứng dụng khách. Nhóm/Map-Reduce cần cung cấp quyền truy cập vào toàn bộ tài liệu tốn kém hơn nhiều so với việc chỉ cung cấp dữ liệu từ chỉ mục (hiện được bao gồm trong 1.7.3+).

Nếu đây là vấn đề chung bạn cần chạy định kỳ, bạn có thể muốn giữ một bộ sưu tập chỉ {md5: value, count: value} để bạn có thể bỏ qua tập hợp và nó sẽ cực kỳ nhanh khi bạn cần phải nhân đôi bản sao.

+0

Tôi không thể sử dụng gruping vì nó bị giới hạn ở 10k yếu tố (tôi có 3M). Nhưng lưu ý rằng MR sẽ chỉ trả về dữ liệu từ chỉ mục là thú vị. Tôi không biết điều đó. Cảm ơn! (+1) –

61

Cá nhân tôi thấy rằng trên cơ sở dữ liệu lớn (1TB và nhiều hơn nữa) chấp nhận câu trả lời là khủng khiếp chậm. Tổng hợp nhanh hơn nhiều. Ví dụ là dưới đây:

db.places.aggregate(
    { $group : {_id : "$extra_info.id", total : { $sum : 1 } } }, 
    { $match : { total : { $gte : 2 } } }, 
    { $sort : {total : -1} }, 
    { $limit : 5 } 
    ); 

Nó tìm kiếm các tài liệu mà extra_info.id được sử dụng hai lần hoặc nhiều lần, loại kết quả theo thứ tự lĩnh vực nhất định và bản in đầu tiên 5 giá trị của nó giảm dần.

+1

Tôi không thấy cách giải pháp của bạn hoạt động trên dữ liệu câu hỏi. Nếu dòng nhóm là '{$ group: {'md5':" $ extra_info.md5 ", tổng cộng: {$ sum: 1}}},'? – zhon

+2

@zhon No. Bạn đã đọc tài liệu chưa? Nó nói 'Đối với trường _id này, bạn có thể chỉ định các biểu thức khác nhau, bao gồm một trường đơn từ tài liệu trong đường ống, giá trị được tính từ giai đoạn trước, tài liệu bao gồm nhiều trường và các biểu thức hợp lệ khác, chẳng hạn như hằng số hoặc trường tài liệu phụ. Bạn có thể sử dụng toán tử $ project trong biểu thức cho trường _id.' – expert

+4

Đối với dữ liệu câu hỏi, dòng nhóm phải là: {$ group: {_id: "$ md5", total: {$ sum: 1}}} , – kdkeck