2012-09-14 23 views
11

私は次のプロジェクトにMongoDBを使用することを検討しています。このアプリケーションのコア要件の1つは、ファセット検索を提供することです。誰かがMongoDBを使ってファセット検索を試みたことがありますか?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 Times:最初に一致結果を取得してからグループごとに1回ずつ検索することです。フルテキスト検索エンジンを使用している間は、すべてを1つのクエリで取得できます。

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

集約フレームワークのようなものが有望と思われるだろう。ファセットグループごとに追加のクエリを実行することに問題はありません。この実装を検証するPOCアプリケーションを作成しましょう。 –

+0

はい、それは本当に強力で、多くの可能性をもたらします。このフレームワークの主な問題は、クエリの最適化です。シャーディングを使用すると、クエリの最適化が不足します。私はこの問題にパッチを当ててgithubで取り上げています。 –

2

MongoDBを持つ、より高度な検索のための人気のオプションは、コミュニティがMongoDB River Pluginをサポートと一緒にElasticSearchを使用することです。 MongoDB River PluginはMongoDBからElasticSearchへの索引付けのためのドキュメントのストリームを送ります。

ElasticSearchはApache Luceneに基づいた分散検索エンジンで、HTTP経由でRESTfulなJSONインターフェイスを備えています。 Facet Search APIと、Percolate"More like this"などのその他の高度な機能があります。

2

あなたは質問をすることができますが、質問は速いかどうかです。つまり、次のようなものです:

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

質問はどのようにパフォーマンスです。この製品には、ファセット検索のための特別な機能はまだありません。道路の向こうには、良いが、tbd/futureである、いくつかの交差点のようなクエリプランがあるかもしれません。

  • プロパティが事前定義されたセットであり、それらが何であるか知っている場合は、それぞれにインデックスを作成できます。現在のインプリメンテーションではインデックスのうちの1つだけが使用されるので、これは役に立ちますが、これまでのところあなたを得ることができます。データセットのサイズが中程度であれば、大丈夫かもしれません。

  • 2つ以上のプロパティを組み合わせた複合インデックスを使用できます。プロパティの数が少ない場合、これはかなりうまくいく可能性があります。インデックスはすべての変数クエリを使用する必要はありませんが、上記のクエリでは、いずれか2つの複合インデックスが1つのアイテムのインデックスよりも優れたパフォーマンスを発揮します。

  • あなたがスキューをあまりにも多く持っていなければ、ブルートフォースが機能します。例えばあなたが1MMスキューであれば、RAMでのテ​​ーブルスキャンは十分に速いかもしれません。この場合、私はファセット値だけでテーブルを作成し、可能な限り小さくし、完全なskuドキュメントを別のコレクションに保管します。例:

    facets_collection: {sz:1、ブランド:123、clr: 'b'、_ id:} ...

場合は、代わりにFACIT寸法の非常に化合物インデックスを作ることができ、あなたが余分な作業をせずに上記と同等になるだろうが高すぎるありえない「ファセット寸法の#。

いくつかのインデックスを作成した場合、多くの場合、それらがもはやラムに収まらないほど多くを作成しないことをお勧めします。

クエリが実行され、それは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) 
} 

この手順を超えて、アプリケーションサーバーは、クライアントに返信する前にカラー/サイズのグループ化を行うことができます。

注 - ファセットとその値を結合するアプローチでは、すべてのファセット値が不規則になり、回避することができます。「MongoDBを使用する主な問題は、N Times:Firstで一致結果を取得し、フルテキスト検索エンジンを使用している間に、すべてを1つのクエリで取得できます。 $ファセット段階ガルシアの答え

3

のMongoDB 3.4が導入

を参照してください、あなたは が 単一の集約段階の中、複数の次元、またはファセットの間でデータを特徴付ける多面的な集計を作成することができます。多面的な集計では、複数のフィルタとカテゴリを使用して、データの参照と分析を支援します。

入力文書は$ facetステージに1回だけ渡されます。

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

あなたはまだ2つの検索を実行する必要がありますが、ドキュメントの正しい1つと関連ファセットのここにある例はありますか? – Ominus

+0

はい...そうですね。 1つのクエリで複数のファセットのユースケースを解決するだけです – Rahul

関連する問題