これは、この質問で概説したプロジェクトの続きです。
私は次のモデルを持っています:
class Product {
public string Id { get; set; }
public string[] Specs { get; set; }
public int CategoryId { get; set; }
}
「Specs」配列は、特殊文字で結合された製品仕様名と値のペアを格納します。たとえば、商品が青色の場合、スペック文字列は「Color〜Blue」になります。このように仕様を表すと、クエリで指定された複数の仕様値を持つ製品をクエリできます。私がサポートしたい2つの主要なクエリがあります:
- 特定のカテゴリのすべての製品を取得します。
- 指定された仕様のセットを持つ特定のカテゴリのすべての製品を取得します。
これはRavenDBでうまく機能します。ただし、特定のクエリを満たす製品に加えて、クエリで指定された製品のセットのすべての仕様の名前と値のペアを含む結果セットを返したいと思います。仕様の名前と値のペアは、仕様の名前と値でグループ化する必要があり、特定の仕様の名前と値のペアを持つ製品の数を含める必要があります。クエリ#1では、次のマップリデュースインデックスを作成しました。
class CategorySpecGroups {
public int CategoryId { get; set; }
public string Spec { get; set; }
public int Count { get; set; }
}
public class SpecGroups_ByCategoryId : AbstractIndexCreationTask<Product, CategorySpecGroups>
{
public SpecGroups_ByCategoryId()
{
this.Map = products => from product in products
where product.Specs != null
from spec in product.Specs
select new
{
CategoryId = product.CategoryId,
Spec = spec,
Count = 1
};
this.Reduce = results => from result in results
group result by new { result.CategoryId, result.Spec } into g
select new
{
CategoryId = g.Key.CategoryId,
Spec = g.Key.Spec,
Count = g.Sum(x => x.Count)
};
}
}
次に、このインデックスをクエリして、特定のカテゴリのすべての仕様名と値のペアを取得できます。私が遭遇している問題は、同じ結果セットを取得することですが、カテゴリと仕様の名前と値のペアのセットの両方でフィルタリングするクエリの場合です。SQLを使用する場合、この結果セットは、カテゴリと仕様でフィルタリングされた製品のセットに対してgroupbyを実行することによって取得されます。一般に、このタイプのクエリはコストがかかりますが、カテゴリと仕様の両方でフィルタリングする場合、製品セットは通常は小さくなりますが、1つのページに収まるほど小さくはありません。最大1000個の製品が含まれる場合があります。参考までに、MongoDBは、同じ結果セットを実現するために使用できるグループメソッドをサポートしています。これにより、アドホックグループ化サーバー側が実行され、パフォーマンスは許容範囲内です。
RavenDBを使用してこのタイプの結果セットを取得するにはどうすればよいですか?
考えられる解決策の1つは、クエリのすべての製品を取得してメモリ内でグループ化を実行することです。別のオプションは、上記のようにmapreduceインデックスを作成することですが、これに伴う課題は、特定のカテゴリに対して実行できるすべての可能な仕様の選択を推測することです。さらに、このタイプのインデックスはサイズが爆発する可能性があります。
例として、このファスナーカテゴリページを見てください。ユーザーは、属性を選択することで選択をフィルタリングできます。属性を選択すると、製品の選択が絞り込まれ、新しい製品セット内の属性が表示されます。このタイプのインタラクションは通常、ファセット検索と呼ばれます。
編集
それまでの間、 Solrを使用して解決策を試みます。これは、ファセット検索をすぐにサポートするためです。
編集2
RavenDBはファセット検索もサポートしているようです(もちろん、これは理にかなっています。インデックスはSolrと同じようにLuceneによって保存されます)。私はこれを調査し、更新を投稿します。
編集3
RavenDBファセット検索機能は期待どおりに機能します。特定のカテゴリ内のクエリのファセットを計算するために使用される各カテゴリIDのファセット設定ドキュメントを保存します。私が今抱えている問題はパフォーマンスです。4500の異なるカテゴリを持つ500kの製品のコレクションの場合、4500のファセット設定ドキュメントが生成されます。カテゴリIDによるクエリは、ファセットをクエリする場合は約16秒、ファセットをクエリしない場合は約0.05秒かかります。テストされた特定のカテゴリには、約6kの製品、23の異なるファセット、および2kの異なるファセットの名前と範囲の組み合わせが含まれています。FacetedQueryRunnerのコードを見た後ファセットクエリは、カウントを取得するためのすべてのファセット名と値の組み合わせに対するLuceneクエリ、および用語を取得するための各ファセット名に対するクエリをもたらすようです。実装の問題の1つは、クエリに関係なく、特定のファセット名のすべての個別の用語を取得することです。これにより、ほとんどの場合、ファセットの用語の数が大幅に減り、Luceneクエリの数が減ります。ここでパフォーマンスを向上させる1つの方法は、ファセット設定ドキュメントごとにMapReduceの計算結果セット(上記のとおり)を保存し、ファセットでさらにフィルタリングするときにすべての個別の用語を取得するためにクエリを実行することです。ただし、全体的なパフォーマンスはまだ遅すぎる可能性があります。