2012-09-14 60 views
11

我打算在下一個項目中使用MongoDB。此應用程序的核心要求之一是提供方面搜索。有沒有人試圖使用MongoDB來實現Facet搜索?使用MongoDB進行方面搜索

我有一個產品模型,有大小,顏色,品牌等各種屬性。在搜索產品時,這個Rails應用程序應該在側邊欄上顯示分面過濾器。小過濾器將是這個樣子:

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) 

回答

17

我認爲使用Apache Solr或ElasticSearch可以獲得更多的靈活性和性能,但這是使用Aggregation Framework支持的。

使用MongoDB的主要問題是你必須查詢N次:第一次獲得匹配結果,然後每組一次;在使用全文搜索引擎的同時,您可以在一個查詢中獲得全部內容。

//'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}} 
) 

一旦用戶使用方面過濾搜索,你必須添加此過濾器來查詢謂詞和匹配謂語如下。

//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

聚合框架很有前途。我沒有看到每個facet組執行額外查詢的問題。讓我創建一個POC應用程序來驗證此實現。 –

+0

是的,它非常強大,它給了我們很多可能性。這個框架的主要問題是查詢優化。使用分片時,缺少查詢優化。我正在修補這個問題並將它在github中引入。 –

2

你可以做這個查詢,問題會不會很快。即類似的:

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

問題是,那麼表現如何。產品中沒有任何特殊的分面搜索功能。在路上可能有一些交集式的查詢計劃很好,但這是tbd /未來。

  • 如果你的屬性是一個預定義的集合,並且你知道它們是什麼,你可以在它們中的每一個上創建一個索引。在當前的實現中,只有一個索引會被使用,所以這會有所幫助,但目前爲止只有這樣:如果數據集大小適中,則可能沒問題。

  • 您可以使用複合索引,這可能會複合兩個或更多屬性。如果你有一小部分屬性,這可能會工作得很好。索引不需要使用所有變量查詢,但是在上面的索引中,任何兩個索引中的複合索引可能比單個索引更好。

  • 如果你沒有太多的skus蠻力會工作;例如如果你是1MM的skam,那麼在ram中的表掃描可能足夠快。在這種情況下,我會製作一張只包含小平面值的表格,並儘可能小,並將完整的sku文檔保存在單獨的集合中。例如:

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

如果小尺寸的心不是」太高,你可能反而使法西特尺寸的高度複合索引,你會得到相當於上面沒有額外工作的#。

如果您創建退出一些索引,最好不要創建太多,他們不再適合內存。

給定查詢運行,這是一個性能問題,可能只是與mongo,如果它不夠快,然後螺栓solr。

0

分面解決方案(基於計數)取決於您的應用程序設計。

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

} 
) 

然而,如果一個人能夠在小面和它們的值被結合在一起以形成一個一致的標籤上面的格式喂數據,則使用下面的查詢

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

查看結果輸出低於

{ 
    "_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) 
} 

除此之外,您的應用程序服務器可以在發送回客戶端之前執行顏色/大小分組。

注 - 組合facet及其值的方法爲您提供了聚合的所有方面值,您可以避免 - 「使用MongoDB的主要問題是您必須查詢N次:第一次獲得匹配結果,然後每組;而在使用全文搜索引擎時,您只需在一個查詢中就可以完成。「看到加西亞的回答

3

的MongoDB 3.4引入了

的$小舞臺,您可以創建多方位的集合,其 多個維度,或面表徵數據,一個 單聚合階段中。多面聚合提供了多個 過濾器和分類來指導數據瀏覽和分析。

輸入文檔只傳遞給$ facet階段一次。

現在,您不需要查詢N次以檢索N個組上的聚合。

$ facet可以在同一組輸入文檔上啓用各種彙總,而不需要多次檢索輸入文檔。

爲OP用例的樣本查詢會是這樣的

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

你仍然需要運行兩個搜索,雖然正確的一個文件,然後你有這裏的關聯facet的例子? – Ominus

+0

是的......看起來像那樣。它只是解決了單個查詢中多個方面的用例 – Rahul