2012-10-07 21 views
5

Mam zbiór dokumentów w następującej formie:Sortowanie według trafności z MongoDB

{ _id: ObjectId(...) 
, title: "foo" 
, tags: ["bar", "baz", "qux"] 
} 

Kwerenda powinna znaleźć wszystkie dokumenty z dowolnego z tych tagów. Obecnie używam tego zapytania:

{ "tags": { "$in": ["bar", "hello"] } } 

I działa; wszystkie dokumenty oznaczone "bar" lub "hello" są zwracane.

Jednak chcę sortować według trafności, tj. Im więcej pasujących tagów to , tym wcześniej dokument powinien pojawić się w wyniku. Na przykład dokument oznaczony ["bar", "hello", "baz"] powinien znajdować się wyżej w wynikach niż dokument oznaczony ["bar", "baz", "boo"] dla zapytania . Jak mogę to osiągnąć?

Odpowiedz

9

MapReduce i robi stronie klienta będzie zbyt wolna - ty . należy użyć ramy agregacji (nowość w MongoDB 2.2)

może to wyglądać mniej więcej tak:

db.collection.aggregate([ 
    { $match : { "tags": { "$in": ["bar", "hello"] } } }, 
    { $unwind : "$tags" }, 
    { $match : { "tags": { "$in": ["bar", "hello"] } } }, 
    { $group : { _id: "$title", numRelTags: { $sum:1 } } }, 
    { $sort : { numRelTags : -1 } } 
    // optionally 
    , { $limit : 10 } 
]) 

Uwaga pierwsze i trzecie elementy rurociągów wyglądać identyczne, jest to celowe i potrzebne. Oto, co należy zrobić:

  1. przekazać tylko dokumenty, które mają w sobie znacznik "bar" lub "cześć".
  2. odpoczynek tablicę tagów (czyli rozłam w jednym dokumencie na element tagów
  3. podanie tylko na znaczniki dokładnie „bar” lub „hello” (czyli wyrzucić resztę znaczników)
  4. grupy według tytułu (może to być również przez „$ _id” lub inna kombinacja oryginalnego dokumentu sumowaniu jak wiele tagów (z „bar” i „Hello”) nie miał
  5. sortowane w kolejności malejącej liczby odpowiednich znaczników
  6. (opcjonalnie) ograniczyć wrócił zestaw do góry 10.
+0

Myślę, że to {$ unwind: "$ tags"}, a nie {$ unwind: {"$ tags"}} –

+0

Co wiesz - masz rację, pierwszy zauważysz za cztery lata? :) –

+0

Przypuszczam. Niesamowita odpowiedź - ogromna pomoc. Dzięki. –

1

Możesz potencjalnie użyć MapReduce do czegoś podobnego. Przetwarzasz każdy dokument na etapie Map, obliczając ile tagów pasuje do zapytania i przypisujesz wynik. Następnie możesz sortować według tego wyniku.

http://www.mongodb.org/display/DOCS/MapReduce

0

Coś że kompleks należy wykonać po zapytań. Albo po stronie serwera poprzez db.eval (jeśli twój klient obsługuje to) lub po stronie klienta. Oto przykład tego, czego szukasz.

Otrzyma wszystkie posty z określonymi znacznikami, a następnie posortuje je według liczby dopasowań.

usunąć db.eva (część i przełożyć je na język klient używa do kwerendy, aby uzyskać efekt stronie klienta (

db.eval(function() { 
    var tags = ["a","b","c"]; 
    return db.posts.find({tags:{$in:tags}}).toArray().sort(function(a,b){ 

     var matches_a = 0; 
     var matches_b = 0; 
     a.tags.forEach(function (tag) { 
      for (t in tags) { 
       if (tag == t) { 
        matches_a++; 
       } else { 
        matches_b++; 
       } 
      } 
     }); 

     b.tags.forEach(function(tag) { 
      for (t in tags) { 
       if (tag == t) { 
        matches_b++; 
       } else { 
        matches_a++; 
       } 
      } 
     }); 
     return matches_a - matches_b; 
    }); 
}); 
+1

To jest powolne dla dużych kolekcji, więc spróbuję wymyślić inną odpowiedź. – arian

Powiązane problemy