2012-09-14 15 views
11

Rozważam użycie MongoDB do mojego następnego projektu. Jednym z podstawowych wymagań tej aplikacji jest wyszukiwanie aspektu. Czy ktoś próbował użyć MongoDB, aby uzyskać wyszukiwanie aspektu?Wyszukiwanie fasetowe przy użyciu MongoDB

Mam model produktu o różnych atrybutach, takich jak rozmiar, kolor, marka itp. Po wyszukaniu produktu ta aplikacja Railsowa powinna pokazać filtry aspektu na pasku bocznym. Filtry aspekt będzie wyglądać mniej więcej tak:

Size: 
XXS (34) 
XS (22) 
S (23) 
M (37) 
L (19) 
XL (29) 

Color: 
Black (32) 
Blue (87) 
Green (14) 
Red (21) 
White (43) 

Brand: 
Brand 1 (43) 
Brand 2 (27) 

Odpowiedz

17

Myślę, że używając Apache Solr lub ElasticSearch zyskujesz większą elastyczność i wydajność, ale jest to obsługiwane za pomocą Aggregation Framework.

Głównym problemem przy użyciu MongoDB jest zapytanie N razy: najpierw dla uzyskania pasujących wyników, a następnie raz dla każdej grupy; podczas korzystania z wyszukiwarki pełnotekstowej dostajesz wszystko w jednym zapytaniu.

Przykład

//'tags' filter simulates the search 
//this query gets the products 
db.products.find({tags: {$all: ["tag1", "tag2"]}}) 

//this query gets the size facet 
db.products.aggregate(
    {$match: {tags: {$all: ["tag1", "tag2"]}}}, 
    {$group: {_id: "$size"}, count: {$sum:1}}, 
    {$sort: {count:-1}} 
) 

//this query gets the color facet 
db.products.aggregate(
    {$match: {tags: {$all: ["tag1", "tag2"]}}}, 
    {$group: {_id: "$color"}, count: {$sum:1}}, 
    {$sort: {count:-1}} 
) 

//this query gets the brand facet 
db.products.aggregate(
    {$match: {tags: {$all: ["tag1", "tag2"]}}}, 
    {$group: {_id: "$brand"}, count: {$sum:1}}, 
    {$sort: {count:-1}} 
) 

Gdy użytkownik filtruje wyszukiwanie za pomocą aspekty, trzeba dodać ten filtr kwerendy orzecznik i mecz orzecznik następująco.

//user clicks on "Brand 1" facet 
db.products.find({tags: {$all: ["tag1", "tag2"]}, brand: "Brand 1"}) 

db.products.aggregate(
    {$match: {tags: {$all: ["tag1", "tag2"]}}, brand: "Brand 1"}, 
    {$group: {_id: "$size"}, count: {$sum:1}}, 
    {$sort: {count:-1}} 
) 

db.products.aggregate(
    {$match: {tags: {$all: ["tag1", "tag2"]}}, brand: "Brand 1"}, 
    {$group: {_id: "$color"}, count: {$sum:1}}, 
    {$sort: {count:-1}} 
) 

db.products.aggregate(
    {$match: {tags: {$all: ["tag1", "tag2"]}}, brand: "Brand 1"}, 
    {$group: {_id: "$brand"}, count: {$sum:1}}, 
    {$sort: {count:-1}} 
) 
+0

Aggregation Framework wydaje się obiecujące. Nie widzę problemu z wykonywaniem dodatkowych zapytań na grupę aspektów. Pozwól mi utworzyć aplikację POC do sprawdzenia poprawności tej implementacji. –

+0

Tak, jest naprawdę potężny i daje nam wiele możliwości. Głównym problemem w tej strukturze jest optymalizacja zapytań. Używanie shardingu oznacza brak optymalizacji zapytań. Pracuję nad naprawianiem tych problemów i wciąganiem ich w github. –

2

Popularnym rozwiązaniem dla poszukiwania bardziej zaawansowanych z MongoDB jest użycie ElasticSearch w połączeniu ze społeczności obsługiwane MongoDB River Plugin. Wtyczka MongoDB River przesyła strumień dokumentów z MongoDB do ElasticSearch w celu zindeksowania.

ElasticSearch jest rozproszoną wyszukiwarką opartą na Apache Lucene i zawiera interfejs RESTful JSON na http. Istnieje Facet Search API i wiele innych zaawansowanych funkcji, takich jak Percolate i "More like this".

2

Możesz zrobić zapytanie, pytanie byłoby szybkie lub nie. czyli coś takiego:

find({ size:'S', color:'Blue', Brand:{$in:[...]} }) 

pytanie brzmi, w jaki sposób jest wydajność. W produkcie nie ma jeszcze specjalnej możliwości wyszukiwania faset. W przyszłości może być kilka dobrze zaplanowanych planów zapytań o skrzyżowanie, ale to jest tbd/future.

  • Jeśli twoje właściwości są predefiniowanym zestawem i wiesz, czym one są, możesz utworzyć indeks na każdym z nich. W bieżącej implementacji zostanie użyty tylko jeden z indeksów, więc to pomoże, ale tylko do tej pory: jeśli zbiór danych jest średniej wielkości, może być w porządku.

  • Można użyć indeksów złożonych, które być może łączą dwie lub więcej właściwości. Jeśli masz małe # właściwości, może to działać całkiem nieźle. Indeks nie musi wykorzystywać wszystkich zapytań zmiennych, ale w powyższym indeks złożony na dowolnych dwóch z trzech może lepiej działać niż indeks na pojedynczym elemencie.

  • Jeśli nie masz zbyt wielu skusów, to brutalna siła zadziała; na przykład jeśli jesteś 1MM skues, skanowanie tabeli w pamięci RAM może być wystarczająco szybkie. w tym przypadku przygotowałbym tabelę z wartościami aspektów i uczynię ją tak małą, jak to tylko możliwe, i zachowam pełną dokumentację sku w oddzielnej kolekcji. np .:

    facets_collection: {sz: 1, marka: 123, clr: 'b', _ id:} ...

jeśli # wymiarów facet isnt”zbyt wysoka można zamiast czynią wysoce złożony indeks FACIT wymiarów i chcesz uzyskać równoważna powyżej bez dodatkowej pracy.

jeśli utworzysz kilka indeksów, prawdopodobnie najlepiej nie tworzyć tak wielu, że nie mieszczą się w pamięci RAM.

biorąc pod uwagę, że zapytanie jest uruchomione i jest to pytanie dotyczące wydajności, które można zrobić z mongo, a jeśli nie jest wystarczająco szybkie, to należy je wcisnąć na solr.

0

Fasetowe rozwiązanie (w oparciu o liczbę) zależy od projektu aplikacji.

db.product.insert(
{ 
tags :[ 'color:green','size:M'] 

} 
) 

Jednakże, jeśli ktoś jest w stanie karmić danych w powyższym formacie gdzie aspekty i ich wartości są połączone ze sobą tworząc spójną znacznik, a następnie za pomocą poniższego zapytania

db.productcolon.aggregate(
    [ 
     { $unwind : "$tags" }, 
     { 
     $group : { 
      _id : '$tags', 
      count: { $sum: 1 } 
     } 
     } 
    ] 
) 

Zobacz wynik uzyskany poniżej:

{ 
    "_id" : "color:green", 
    "count" : NumberInt(1) 
} 
{ 
    "_id" : "color:red", 
    "count" : NumberInt(1) 
} 
{ 
    "_id" : "size:M", 
    "count" : NumberInt(3) 
} 
{ 
    "_id" : "color:yellow", 
    "count" : NumberInt(1) 
} 
{ 
    "_id" : "height:5", 
    "count" : NumberInt(1) 
} 

Po tym etapie serwer aplikacji może wykonać grupowanie kolorów/rozmiarów przed wysłaniem z powrotem do klienta.

Uwaga - Podejście do łączenia aspektu i jego wartości daje wszystkie wartości aspektów zgrupowane i można uniknąć - "Głównym problemem przy użyciu MongoDB jest zapytanie N razy: najpierw dla uzyskania wyników dopasowujących, a następnie raz dla grupy ; podczas korzystania z wyszukiwarki pełnotekstowej dostajesz wszystko w jednym zapytaniu. " patrz odpowiedź Garcii

3

MongoDB 3.4 wprowadza

$ etap aspekt pozwala na tworzenie wielopłaszczyznowych agregacje który charakteryzują danych w wielu wymiarach, lub aspektów, w jednym etapie agregacji. Agregacje wieloaspektowe zapewniają wiele filtrów i kategoryzacji, które ułatwiają przeglądanie i analizę danych.

Dokumenty wejściowe są przekazywane do etapu $ facet tylko raz.

Teraz nie musisz pytać N razy o pobieranie agregacji w grupach N.

$ facet umożliwia różne agregacje na tym samym zestawie dokumentów wejściowych, bez konieczności wielokrotnego pobierania dokumentów wejściowych.

Zapytanie próbki dla PO użytkowej przypadku byłoby coś

db.products.aggregate([ 
    { 
    $facet: { 
     "categorizedByColor": [ 
     { $match: { color: { $exists: 1 } } }, 
     { 
      $bucket: { 
      groupBy: "$color", 
      default: "Other", 
      output: { 
       "count": { $sum: 1 } 
      } 
      } 
     } 
     ], 
     "categorizedBySize": [ 
     { $match: { size: { $exists: 1 } } }, 
     { 
      $bucket: { 
      groupBy: "$size", 
      default: "Other", 
      output: { 
       "count": { $sum: 1 } 
      } 
      } 
     } 
     ], 
     "categorizedByBrand": [ 
     { $match: { brand: { $exists: 1 } } }, 
     { 
      $bucket: { 
      groupBy: "$brand", 
      default: "Other", 
      output: { 
       "count": { $sum: 1 } 
      } 
      } 
     } 
     ] 
    } 
    } 
]) 
+0

nadal musiałbyś przeprowadzić dwa wyszukiwania, ale poprawny dla dokumentów, a następnie przykład, który tutaj masz dla powiązanych aspektów? – Ominus

+0

Tak ... wydaje się, że tak. Po prostu rozwiązuje przypadek użycia dla wielu aspektów w pojedynczym zapytaniu – Rahul