这是此问题中概述的项目的延续。
我有以下模型:
class Product {
public string Id { get; set; }
public string[] Specs { get; set; }
public int CategoryId { get; set; }
}
“规格”数组存储由特殊字符连接的产品规格名称值对。例如,如果产品颜色为蓝色,则规格字符串将为“Color~Blue”。以这种方式表示规格允许查询具有由查询指定的多个规格值的产品。我想支持两个主要查询:
- 获取给定类别中的所有产品。
- 获取给定类别中具有一组指定规格的所有产品。
这适用于 RavenDB。但是,除了满足给定查询的产品之外,我还想返回一个结果集,其中包含查询指定的产品集的所有规范名称-值对。规范名称-值对应按规范的名称和值分组,并包含具有给定规范名称-值对的产品的计数。对于查询 #1,我创建了以下 map reduce 索引:
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 支持组方法,可用于实现相同的结果集。这将执行 ad hoc 分组服务器端并且性能是可以接受的。
如何使用 RavenDB 获得这种类型的结果集?
一种可能的解决方案是获取查询的所有产品并在内存中执行分组,另一种选择是如上所述创建 mapreduce 索引,尽管这样做的挑战是推导出可以为给定类别进行的所有可能的规范选择此外,此类索引的大小可能会爆炸式增长。
例如,看看这个紧固件类别页面。用户可以通过选择属性来过滤他们的选择。选择属性时,它会缩小产品的选择并显示新产品集中的属性。这种类型的交互通常称为分面搜索。
编辑
同时,我将尝试使用Solr的解决方案,因为它们支持开箱即用的分面搜索。
编辑 2
RavenDB 似乎也支持分面搜索(这当然是有道理的,索引由 Lucene 存储,就像 Solr 一样)。我将对此进行探索并发布更新。
编辑 3
RavenDB 分面搜索功能按预期工作。我为每个类别 ID 存储一个构面设置文档,用于计算给定类别中查询的构面。我现在遇到的问题是性能。对于具有 4500 个不同类别的 500k 产品的集合,产生 4500 个方面设置文档,按类别 id 的查询在查询方面时大约需要 16 秒,而在不查询方面时大约需要 0.05 秒。测试的特定类别包含大约 6k 个产品、23 个不同的 facet 和 2k 个不同的 facet name-range 组合。在查看FacetedQueryRunner中的代码后似乎一个方面查询将导致对每个方面名称-值组合的 Lucene 查询以获取计数,以及对每个方面名称的查询以获取术语。实现的一个问题是,无论查询如何,它都会检索给定构面名称的所有不同术语,这在大多数情况下会显着减少构面的术语数量,从而减少 Lucene 查询的数量。这里提高性能的一种方法是为每个构面设置文档存储一个 MapReduce 计算结果集(如上所示),然后可以在进一步按构面过滤时查询该结果集以获取所有不同的术语。然而,整体性能可能仍然太慢。