14

次のプロジェクトで MongoDB を使用することを検討しています。このアプリケーションの主要な要件の 1 つは、ファセット検索を提供することです。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)
4

5 に答える 5

21

Apache Solr または ElasticSearch を使用すると、柔軟性とパフォーマンスが向上すると思いますが、これはAggregation Frameworkを使用してサポートされています。

MongoDB を使用する主な問題は、N 回クエリを実行する必要があることです。最初は一致する結果を取得するため、次にグループごとに 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}}
)
于 2012-09-15T07:56:16.690 に答える
15

Mongodb 3.4 でファセット検索が導入されました

$facet ステージを使用すると、1 つの集計ステージ内で複数のディメンションまたはファセットにわたってデータを特徴付ける多面的な集計を作成できます。多面的な集計は、データの閲覧と分析をガイドする複数のフィルターと分類を提供します。

入力ドキュメントは $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 }
            }
          }
        }
      ]
    }
  }
])
于 2016-12-24T04:15:10.197 に答える
3

MongoDB でより高度な検索を行うための一般的なオプションは、コミュニティがサポートするMongoDB River Pluginと組み合わせてElasticSearchを使用することです。MongoDB River プラグインは、インデックス作成のために MongoDB から ElasticSearch にドキュメントのストリームをフィードします。

ElasticSearch は、 Apache Luceneに基づく分散検索エンジンであり、http を介した RESTful JSON インターフェイスを備えています。ファセット検索 APIと、 Percolate"More like this"などのその他の高度な機能が多数あります。

于 2012-09-14T21:10:44.430 に答える
2

クエリを実行できますが、問題は高速かどうかです。つまり、次のようなものです:

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

問題は、パフォーマンスがどうであるかです。この製品には、ファセット検索のための特別な機能はまだありません。将来的には、いくつかの交差点のようなクエリ プランが設定される可能性がありますが、それは未定/将来です。

  • プロパティが定義済みのセットであり、それらが何であるかを知っている場合は、それぞれにインデックスを作成できます。現在の実装ではインデックスの 1 つだけが使用されるため、これは役に立ちますが、これまでのところしか取得できません。データ セットが中規模以上のサイズであれば、問題ない可能性があります。

  • おそらく 2 つ以上のプロパティを複合する複合インデックスを使用できます。プロパティの数が少ない場合、これはかなりうまくいくかもしれません。インデックスはクエリのすべての変数を使用する必要はありませんが、上記の 1 つでは、3 つのうちの任意の 2 つの複合インデックスは、単一項目のインデックスよりもパフォーマンスが優れている可能性があります。

  • sku が多すぎない場合は、ブルート フォースが機能します。たとえば、1MM skues の場合、RAM でのテーブル スキャンは十分に高速である可能性があります。この場合、ファセット値のみを含むテーブルを作成し、できるだけ小さくして、完全な SKU ドキュメントを別のコレクションに保持します。例えば:

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

ファセット ディメンションの数が多すぎない場合は、代わりにファシット ディメンションの高度に複合的なインデックスを作成することができ、余分な作業を行わなくても上記と同等の結果が得られます。

いくつかのインデックスを作成する場合は、RAM に収まらないほど多く作成しないことをお勧めします。

クエリが実行され、それがパフォーマンスの問題である場合、mongo を使用するだけで十分な速度が得られない場合は、solr を追加します。

于 2012-09-15T15:41:21.797 に答える
1

ファセット ソリューション (カウント ベース) は、アプリケーションの設計によって異なります。

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 回クエリを実行する必要があることです: 最初に一致する結果を取得するため、次にグループごとに 1 回。 1 回のクエリですべて取得できる全文検索エンジンです。」ガルシアの答えを見てください

于 2016-02-16T14:56:20.327 に答える