2011-12-23 34 views
8

Với bộ sưu tập sau MongoDB của tài liệu:Nhận tài liệu với thẻ trong danh sách, sắp xếp theo tổng số các trận đấu

{ 
title : 'shirt one' 
tags : [ 
    'shirt', 
    'cotton', 
    't-shirt', 
    'black' 
] 
}, 
{ 
title : 'shirt two' 
tags : [ 
    'shirt', 
    'white', 
    'button down collar' 
] 
}, 
{ 
title : 'shirt three' 
tags : [ 
    'shirt', 
    'cotton', 
    'red' 
] 
}, 
... 

Làm thế nào để bạn lấy một danh sách các mục phù hợp với danh sách các thẻ, ra lệnh cho tổng số thẻ phù hợp? Ví dụ, đưa ra danh sách các thẻ như là đầu vào:

['shirt', 'cotton', 'black'] 

Tôi muốn để lấy các mặt hàng xếp theo thứ tự giảm dần theo tổng số phù hợp với thẻ:

item   total matches 
--------  -------------- 
Shirt One  3 (matched shirt + cotton + black) 
Shirt Three 2 (matched shirt + cotton) 
Shirt Two  1 (matched shirt) 

Trong một lược đồ quan hệ, thẻ sẽ là một bảng riêng biệt và bạn có thể tham gia vào bảng đó, đếm các trận đấu và sắp xếp theo số lượng.

Nhưng, ở Mongo ...?

Có vẻ phương pháp này có thể làm việc,

  • phá vỡ các thẻ đầu vào thành nhiều "IN" báo cáo
  • truy vấn cho các mục bằng cách "OR" 'ing cùng các đầu vào thẻ
    • tức là nơi (' áo sơ mi 'IN items.tags) HOẶC (' cotton 'IN items.tags)
    • điều này sẽ trở lại, ví dụ: ba trường hợp "Áo một", 2 trường hợp "Áo ba", v.v.
  • bản đồ/giảm sản lượng
    • bản đồ: emit (this._id, {...});
    • giảm: đếm tổng số lần xuất hiện của _id
    • Finalize: Sắp xếp theo tính tổng

Nhưng tôi không rõ ràng về cách thực hiện điều này như một truy vấn Mongo, hoặc nếu điều này là ngay cả những cách tiếp cận hiệu quả nhất.

+0

Có vẻ như công việc M/R đơn giản. –

+1

Không có M/R là đơn giản trong mã sản xuất kể từ khi thực hiện hiện tại thiếu song song thích hợp. Trong thực tế, một trường hợp tốt có thể được thực hiện để tránh m/r hoàn toàn trong các tình huống thông lượng cao. –

Trả lời

5

Hiện tại, bạn không thể làm gì trừ khi bạn sử dụng MapReduce. Vấn đề duy nhất với MapReduce là vấn đề chậm (so với truy vấn thông thường).

Khung tổng hợp được dự kiến ​​cho 2.2 (vì vậy sẽ có sẵn trong bản phát hành 2.1 dev) và sẽ giúp việc này trở nên dễ dàng hơn nhiều nếu không có MapReduce.

Cá nhân, tôi không nghĩ rằng việc sử dụng M/R là một cách hiệu quả để làm điều đó. Tôi thích truy vấn tất cả các tài liệu và thực hiện các tính toán đó ở phía ứng dụng. Việc quy mô các máy chủ ứng dụng của bạn dễ dàng hơn và rẻ hơn so với quy mô của máy chủ cơ sở dữ liệu của bạn để cho các máy chủ ứng dụng thực hiện việc crunching số. Trong số đó, cách tiếp cận này có thể không hoạt động cho bạn do các mẫu và yêu cầu truy cập dữ liệu của bạn.

Một cách tiếp cận thậm chí đơn giản có thể chỉ bao gồm một tài sản count trong mỗi đối tượng thẻ của bạn và bất cứ khi nào bạn $push một thẻ mới để mảng, bạn cũng $inc các count tài sản. Đây là một mô hình phổ biến trong thế giới MongoDB, ít nhất là cho đến khi khuôn khổ tổng hợp.

+1

Bao gồm một thuộc tính đếm khi $ push'ing một thẻ mới vào mảng sẽ không giúp được vấn đề này, vì wount chỉ đơn giản có thể cho biết tổng số thẻ (không phải tổng số thẻ khớp với đầu vào). – Matt

+0

Ah đúng, tôi đã đi trước bản thân mình ở đó. –

1

Tôi sẽ là người thứ hai @Bryan khi nói rằng MapReduce là cách duy nhất có thể vào lúc này (và nó hoàn toàn xa vời).Nhưng, trong trường hợp bạn rất cần nó, here you go :-)

var m = function() { 
     var searchTerms = ['shirt', 'cotton', 'black']; 
     var me = this; 
     this.tags.forEach(function(t) { 
      searchTerms.forEach(function(st) { 
       if(t == st) { 
        emit(me._id, {matches : 1}); 
       } 
      }) 
     }) 
    }; 

    var r = function(k, vals) { 
     var result = {matches : 0}; 
     vals.forEach(function(v) { 
      result.matches += v.matches; 
     }) 
     return result; 
    }; 

    db.shirts.mapReduce(m, r, {out: 'found01'}); 

    db.found01.find(); 
+0

Cảm ơn, đây là một khởi đầu tốt. Nhưng, thay vì chạy bản đồ/giảm trên * tất cả * mục trong bộ sưu tập, sẽ không nhanh hơn nếu bạn thực hiện tìm kiếm ban đầu bằng OR'ing cùng với các thẻ nhập liệu? Điều này sẽ làm giảm kích thước của tập hợp được xử lý bằng m(), và r() chỉ đơn giản có thể trả về vals.length như tổng số kết quả khớp? – Matt

7

Như tôi đã trả lời trong In MongoDB search in an array and sort by number of matches

Có thể sử dụng tập hợp khung.

Giả

  • tags thuộc tính là một tập hợp (không có yếu tố lặp đi lặp lại)

Query lực lượng

Cách tiếp cận này bạn để tháo gỡ những kết quả và đánh giá lại vị phù hợp với kết quả được giải phóng, vì vậy nó thực sự không hiệu quả.

db.test_col.aggregate(
    {$match: {tags: {$in: ["shirt","cotton","black"]}}}, 
    {$unwind: "$tags"}, 
    {$match: {tags: {$in: ["shirt","cotton","black"]}}}, 
    {$group: { 
     _id:{"_id":1}, 
     matches:{$sum:1} 
    }}, 
    {$sort:{matches:-1}} 
); 

Kết quả mong đợi

{ 
    "result" : [ 
     { 
      "_id" : { 
       "_id" : ObjectId("5051f1786a64bd2c54918b26") 
      }, 
      "matches" : 3 
     }, 
     { 
      "_id" : { 
       "_id" : ObjectId("5051f1726a64bd2c54918b24") 
      }, 
      "matches" : 2 
     }, 
     { 
      "_id" : { 
       "_id" : ObjectId("5051f1756a64bd2c54918b25") 
      }, 
      "matches" : 1 
     } 
    ], 
    "ok" : 1 
} 
+0

Samuel Answer là chính xác. Tôi chỉ tranh chấp các thông tin bổ sung mà nó không hiệu quả. Để phù hợp với ai đó sẽ phải giải phóng các thẻ dù sao thực hiện nhiệm vụ này trong đường ống tổng hợp có thể là cách tiếp cận nhanh nhất cho truy vấn adhoc – rat

+0

Câu trả lời này có hiệu quả đối với tôi tuy nhiên tôi đã phải thực hiện một thay đổi nhỏ trong đối tượng '$ group' để thực hiện công việc này ở Mongo 3.0. và sử dụng ID này cho ID '_id: {" _ id ":" $ _ id "}' – Binarytales

+0

Có, thực sự. Định dạng _id nhóm đã thay đổi trong phiên bản 3.0 và bây giờ bạn có thể sử dụng định dạng đó hoặc định dạng lồng nhau nhưng cũng có ký hiệu $. –

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