2011-10-03 13 views
5

これはthisで概説されたプロジェクトの続きです。製品カタログの照会製品の任意のコレクションに対するスペック集約のためのRavenDBストア

私は、次のモデルがあります:

class Product { 
    public string Id { get; set; } 
    public string[] Specs { get; set; } 
    public int CategoryId { get; set; } 
} 

「仕様」の配列に格納の特殊文字によって接合された製品仕様名と値のペアを。例えば、製品が青色に着色されている場合、仕様文字列は「Color〜Blue」になります。このように仕様を表すことで、クエリで指定された複数の仕様値を持つ製品をクエリできます。私がサポートしたい2つの主要な質問があります:

  1. 特定のカテゴリのすべての製品を取得します。
  2. 指定されたカテゴリのすべての製品が、指定された仕様のセットを取得します。

これはRavenDBでうまくいきます。しかし、特定のクエリを満たす製品に加えて、クエリで指定された製品のセットのすべての仕様の名前と値のペアを含む結果セットを返したいと思います。スペック名と値のペアは、スペックの名前と値でグループ化し、指定されたスペック名と値のペアを持つ製品の数を含む必要があります。私は、このインデックスを照会し、特定のカテゴリ内のすべてのスペックの名前と値のペアを取得することができます

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) 
           }; 
    } 
} 

:クエリ#1のために私は、次のマップは、インデックスを減らす作成しました。私が実行している問題は、同じ結果セットを取得することですが、カテゴリと仕様の名前と値のペアの両方をフィルタリングするクエリに対してです。 SQLを使用する場合、この結果セットは、カテゴリと仕様でフィルタリングされた一連の製品でグループを実行することによって得られます。一般に、このタイプのクエリは高価ですが、カテゴリと仕様の両方でフィルタリングすると、製品セットは通常小さくなりますが、単一のページに収まるほど小さくはありません。参考までに、MongoDBは同じ結果セットを得るために使用できるgroupメソッドをサポートしています。これにより、アドホックグループ化サーバー側が実行され、パフォーマンスは許容されます。

RavenDBを使用してこのタイプの結果セットを取得するにはどうすればよいですか?

可能な解決策の1つは、クエリですべての製品を取得してメモリ内でグループ分けを実行することです。これを使用すると、可能なすべてのスペック選択を推測することができます特定のカテゴリの場合、さらにこのタイプのインデックスはサイズが爆発する可能性があります。

たとえば、this fastener category pageをご覧ください。ユーザーは、属性を選択することによって選択をフィルタリングできます。属性を選択すると、製品の選択が絞り込まれ、新しい製品セット内の属性が表示されます。この種の相互作用は、通常faceted searchと呼ばれます。

EDIT

その間に彼らは箱から出しファセット検索をサポートするように、私はSolrを使用してソリューションをしようとします。

EDIT 2

RavenDBも(もちろん理にかなっている、インデックスがちょうどSolrのようLuceneとによって保存されている)faceted searchをサポートしていることが表示されます。私はこれを探求し、更新を掲載する予定です。予想通り

EDIT 3

RavenDBファセット検索機能は動作します。私は、各カテゴリIDのファセット設定文書を保存します。これは、特定のカテゴリ内のクエリのファセットを計算するために使用されます。今私が持っている問題はパフォーマンスです。 4500個の異なるカテゴリを持つ500k製品のコレクションでは、4500個のファセット設定ドキュメントが生成され、カテゴリIDによるクエリでは、ファセットを照会する場合は約16秒、ファセットを照会しない場合は約0.05秒かかります。テストされた特定のカテゴリには、約6kの製品、23の異なるファセット、2kの異なるファセットの名前範囲の組み合わせが含まれています。 FacetedQueryRunnerのコードを調べると、ファセットクエリーが表示され、各ファセット名と値の組み合わせごとにカウントを取得し、各ファセット名のクエリーとして結果を取得します。この実装の1つの問題は、ほとんどの場合、ファセットの用語数を大幅に減らし、したがってLuceneクエリの数を減らす、クエリに関係なく、指定されたファセット名のすべての別個の用語を検索することです。ここでのパフォーマンスを向上させる方法の1つは、各ファセット設定ドキュメント用のMapReduce計算結果セット(上記のように)を保存することです。ファセットによってさらにフィルタリングするときにすべての別個の用語を取得することができます。しかし、全体的なパフォーマンスはまだ遅すぎるかもしれません。

+0

私は理解しているとは思わない。作成するクエリは何ですか? 期待される結果は何ですか? –

+0

たとえば、カテゴリidで定義された一連の製品 - 特定のカテゴリのすべての製品を取り上げます。セット内の各製品には、指定された名前値のペアを持つ製品の数を数えて名前と値でグループ化することができる仕様の名前と値のペアのセットがあります。これは私が得たい結果ですが、特定のカテゴリの製品だけでなく、一連のスペック名と値のペアでフィルタリングされたカテゴリのカテゴリも対象となります。これは、ウェブサイトの製品カタログ上の製品選択を「穴あけ」することを可能にすることである。 – eulerfx

答えて

3

RavenDB faceted searchを使用してこの機能を実装しましたが、ヒューリスティックな最適化をサポートするためにFacetedQueryRunnerにいくつか変更を加えました。ヒューリスティックなのは、私の場合、ファセットは葉のカテゴリにしか表示されないということです。ルートカテゴリと内部カテゴリの間のナビゲーションは、検索または子カテゴリのリストによって駆動されるため、これは妥当な制約です。

制約が与えられたので、私は "facets/category_123"のようなIdを持つ各リーフカテゴリに対してFacetSetupドキュメントを保存します。ファセット設定文書が保存されているとき、私はファセット名とそのカテゴリに含まれるファセット値(または範囲)にアクセスできます。したがって、FacetSetupドキュメントの各ファセットのRangesコレクションに使用可能なファセット値をすべて格納できますが、ファセットモードはまだFacetMode.Defaultです。

Here are FacetedQueryRunnerの変更。具体的には、最適化では、指定されたファセットが範囲を格納しているかどうかを確認します。その場合、指定されたファセットに関連付けられたインデックス内のすべての用語を取得するのではなく、ほとんどの場合、特定のカテゴリに利用可能なファセット値はインデックス全体のファセット値のサブセットであるため、必要なLucene検索の数が大幅に削減されます。

次の最適化は、元のクエリがカテゴリIDだけでフィルタリングする場合、FacetSetupドキュメントでも実際にカウントを保存できることです。これを行うには、Rangesコレクションの各ファセット値にカウントを追加してから、FacetSetupドキュメントにブール値を追加して、カウントが追加されたことを示します。これで、このファセットクエリは、基本的にファセットセットドキュメントの値を返します。クエリする必要はありません。

ここでは、FacetSetupドキュメントを最新の状態に保つことが検討されていますが、これはいずれかの方法で必要となります。この最適化以外にも、キャッシングを利用することができます。これは、Solrファセット検索の手法を信じています。

また、FacetSetupドキュメントが製品コレクションと自動的に同期する場所であれば、効果的です。なぜなら、最初にカテゴリIDでグループ化したMapReduce操作の結果であり、次にファセット名その価値。

関連する問題