2011-10-03 26 views
5

這是this問題概述的項目的延續。查詢產品目錄RavenDB專門針對任意集合產品的規格彙總

我有以下模式:

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

的「規格」數組存儲產品規格名稱值由一個特殊的字符加入對。例如,如果產品是藍色的,則規格字符串將是「顏色〜藍色」。以這種方式表示規格允許查詢具有由查詢指定的多個規格值的產品。我希望支持兩種主要查詢:

  1. 獲取給定類別中的所有產品。
  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時,通過按類別和規格過濾的一組產品進行分組可獲得此結果集。一般來說,這種類型的查詢很昂貴,但是當按照類別和規格進行過濾時,產品集通常很小,但不足以放入單個頁面 - 它們可能包含多達1000種產品。作爲參考,MongoDB支持可用於實現相同結果集的group方法。這將執行ad hoc分組服務器端,性能是可以接受的。

如何使用RavenDB獲得這種類型的結果集?

一個可能的解決方案是獲取查詢的所有產品並在內存中執行分組,另一個選項是創建一個如上所述的mapreduce索引,但挑戰是推導出可以進行的所有可能的spec選擇對於給定的類別,此外,此類索引的大小可能會爆炸。

舉個例子,看看this fastener category page。用戶可以通過選擇屬性來過濾他們的選擇。選擇屬性後,會縮小產品的選擇範圍,並在新的一組產品中顯示屬性。這種交互通常稱爲faceted search

編輯

在此期間,他們支持面搜索開箱我將嘗試使用Solr的解決方案。

編輯2

似乎RavenDB還支持faceted search(這當然是有道理的,索引由Lucene的存儲一樣的Solr)。我將會探索這個併發布更新。

編輯3

的RavenDB面搜索功能按預期工作。我爲每個類別ID存儲一個構面設置文檔,用於計算給定類別內的查詢的構面。我現在面臨的問題是表現。對於具有4500個不同類別的500k產品的集合導致4500個構面文檔設置文檔,查詢分類id在查詢構面時大約需要16秒,而在不查詢構面時大約需要0.05秒。測試的特定類別包含約6k產品,23個不同的刻面和2k個不同刻面名稱範圍組合。查看FacetedQueryRunner中的代碼後,出現一個facets查詢將導致每個方面名稱 - 值組合的Lucene查詢獲取計數,以及每個方面名稱的查詢以獲取這些術語。實現的一個問題是,它將檢索給定方面名稱的所有不同的術語,而不考慮查詢,這在大多數情況下會顯着減少方面的術語數量,並因此減少Lucene查詢的數量。提高性能的方法之一是爲每個分面設置文檔存儲一個MapReduce計算結果集(如上所示),然後可以查詢這些文檔,以便在按分面進一步過濾時獲取所有不同的術語。但整體表現可能仍然太慢。

+0

我不認爲我理解。你想要做什麼查詢? 預期結果是什麼? –

+0

以例如由類別id定義的產品集 - 給定類別中的所有產品。該集合中的每個產品都有一組規格名稱 - 值對,可以按名稱和值對它們進行分組,計數具有給定名稱值對的產品數量。這是我希望得到的結果,但不僅適用於給定類別的產品,還適用於通過一組規格名稱 - 值對過濾的類別中的類別。這是爲了允許在網站產品目錄上「鑽取」產品選擇。 – eulerfx

回答

3

我已經使用RavenDB faceted search實現了此功能,但是我對FacetedQueryRunner進行了一些更改以支持啓發式優化。啓發式是,在我的情況下,facet只顯示在葉子類別中。這是一個合理的限制,因爲根類別和內部類別之間的導航可以由搜索或子類別列表驅動。

現在給出約束條件,我爲每個葉子類別存儲一個FacetSetup文檔,其中Id就像「facets/category_123」一樣。在存儲構面設置文檔時,我可以訪問構面名稱以及該類別中包含的構面值(或範圍)。因此,我可以將所有可用的構面值存儲在FacetSetup文檔中每個構面的Ranges集合中,但構面模式仍然是FacetMode.Default。

Here are對FacetedQueryRunner的更改。具體而言,優化檢查以查看給定方面是否存儲範圍,在這種情況下,它會返回這些值以用於搜索,而不是獲取與給定方面關聯的索引中的所有項。在大多數情況下,這會顯着減少所需的Lucene搜索的數量,因爲可用的分類值在整個索引中是分面值的一個子集。

可以進行的下一個優化是,如果原始查詢只按類別id進行過濾,那麼FacetSetup文檔實際上也可以存儲計數。其中之一,儘管hacky,做到這一點的方法是將計數附加到Ranges集合中的每個facet值,然後將一個布爾值添加到FacetSetup文檔以指示附加計數。現在,這方面的查詢將基本上返回FacetSetup文檔中的值 - 無需查詢。

現在的一個考慮是保持FacetSetup文件是最新的,但這將是必需的。除了這個優化緩存可以被利用,這是我相信Solr分面搜索採取的方法。

此外,如果FacetSetup文檔會自動與產品集合同步,因爲它們是對產品集合進行聚合MapReduce操作的結果,因爲它們最初是按類別ID分組的,然後是分面的名稱,然後價值。