分面
分面是基于索引词将搜索结果排列成类别的过程。
搜索者会看到索引词,以及每个词找到的匹配文档数量的数字计数。分面使用户可以轻松地浏览搜索结果,缩小到他们正在寻找的确切结果。
另请参阅 JSON 分面 API,了解此方法的另一种方法。
通用分面参数
有两个用于控制分面的通用参数。
facet
-
必需
默认值:
false
如果设置为
true
,则此参数会在查询响应中启用分面计数。如果设置为false
、空白值或缺失值,则此参数禁用分面。除非此参数设置为true
,否则下面列出的其他参数均无效。 facet.query
-
可选
默认值:无
在 Lucene 默认语法中指定任意查询以生成分面计数。
默认情况下,Solr 的分面功能会自动确定字段的唯一词项,并返回每个词项的计数。使用
facet.query
,您可以覆盖此默认行为,并准确选择您希望计数的词项或表达式。在典型的分面实现中,您将指定多个facet.query
参数。此参数对于基于数值范围的分面或基于前缀的分面特别有用。您可以多次设置
facet.query
参数,以指示应将多个查询用作单独的分面约束。要使用默认语法以外的语法中的分面查询,请在分面查询前加上查询表示法的名称。例如,要使用假设的
myfunc
查询解析器,您可以像这样设置facet.query
参数facet.query={!myfunc}name~fred
字段值分面参数
可以使用多个参数来根据字段中索引的词项触发分面。
使用这些参数时,请务必记住,“词项”在 Lucene 中是一个非常具体的概念:它与在任何分析发生后索引的文字字段/值对相关。对于包含词干提取、小写或单词拆分的文本字段,生成的词项可能与您预期的不同。
如果您希望 Solr 对完整的文字字符串执行分析(用于搜索)和分面,请在您的模式中使用 copyField
指令来创建字段的两个版本:一个 Text 和一个 String。如果用于搜索但不用于分面,则 Text 字段应具有 indexed="true" docValues="false"
;如果用于分面但不用于搜索,则 String 字段应具有 indexed="false" docValues="true"
。(有关 copyField
指令的更多信息,请参阅 复制字段。)
除非另有说明,否则可以使用 f.<fieldname>.facet.<parameter>
的语法在每个字段的基础上指定以下所有参数
facet.field
-
可选
默认值:无
标识应被视为分面的字段。它遍历字段中的每个词项,并使用该词项作为约束来生成分面计数。可以在查询中多次指定此参数,以选择多个分面字段。
如果您未将此参数设置为模式中的至少一个字段,则本节中描述的其他参数均无效。 facet.prefix
-
可选
默认值:无
将分面的词项限制为以给定字符串前缀开头的词项。这不会以任何方式限制查询,只会限制响应查询返回的分面。
facet.contains
-
可选
默认值:无
限制用于分面的词项为包含给定子字符串的词项。这不会以任何方式限制查询,而只会限制响应查询时返回的分面。
facet.contains.ignoreCase
-
可选
默认值:无
如果设置为
true
,则在将facet.contains
子字符串与候选分面词项进行匹配时,将忽略大小写。 facet.matches
-
可选
默认值:无
仅返回与此正则表达式匹配的词项的分面桶。
facet.sort
-
可选
默认值:无
分面字段词项的排序方式。有两种选项
count
-
返回按计数从高到低排序的词项。
index
-
返回按字典顺序排序的词项。对于 ASCII 范围内的词项,将按字母顺序排序。
如果
facet.limit
大于 0,则默认值为count
,否则默认值为index
。请注意,当使用特定词项限制分面时,默认逻辑会发生更改。
facet.limit
-
可选
默认值:
100
返回的分面计数数量。负值表示 Solr 将返回所有计数。
facet.offset
-
可选
默认值:
0
返回的分面列表的偏移量,以允许分页。
facet.mincount
-
可选
默认值:
0
分面字段包含在响应中所需的最小计数。如果字段的计数低于最小值,则不会返回该字段的分面。
facet.missing
-
可选
默认值:
false
如果设置为
true
,则此参数指示,除了分面字段基于词项的约束之外,还应计算并返回响应中匹配查询但该字段没有分面值的所有结果的计数。 facet.method
-
可选
默认值:
fc
选择分面字段时使用的算法或方法类型。
以下方法可用。
enum
-
枚举字段中的所有词项,计算匹配该词项的文档与匹配查询的文档的集合交集。
建议对只有少量不同值的多值字段进行分面时使用此方法。每个文档的平均值数量无关紧要。
例如,在包含美国州(例如
Alabama、Alaska、… Wyoming
)的字段上进行分面,将导致 50 个缓存的过滤器被重复使用。filterCache
应该足够大,以容纳所有缓存的过滤器。 fc
-
通过迭代匹配查询的文档并对每个文档中出现的词项求和来计算分面计数。
如果字段是多值的或者被标记化(根据
FieldType.isTokened()
),则当前使用UnInvertedField
缓存来实现此功能。在缓存中查找每个文档以查看它包含哪些词项/值,并为每个值递增计数。此方法非常适合字段的索引值数量很多,但每个文档的值数量很少的情况。对于多值字段,使用一种混合方法,该方法对匹配许多文档的词项使用来自
filterCache
的词项过滤器。字母fc
代表字段缓存。 fcs
-
用于单值字符串字段的每个段的字段分面。使用
facet.method=fcs
启用,并使用threads
本地参数控制使用的线程数。此参数允许在快速索引更改的情况下更快地进行分面。
默认值为
fc
(使用BoolField
字段类型以及请求facet.exists=true
时除外),因为它往往占用较少的内存,并且当字段在索引中有很多唯一词项时速度更快。 facet.enum.cache.minDf
-
可选
默认值:
0
指示用于确定该词项的约束计数时应使用 filterCache 的最小文档频率(匹配词项的文档数)。这仅用于
facet.method=enum
分面方法。大于零的值会减少 filterCache 的内存使用量,但会增加处理查询所需的时间。如果要在具有大量词项的字段上进行分面,并且希望减少内存使用量,请尝试将此参数设置为
25
到50
之间的值,并运行一些测试。然后,根据需要优化参数设置。默认值为
0
,导致 filterCache 用于字段中的所有词项。 facet.exists
-
可选
默认值:无
要将分面计数限制为 1,请指定
facet.exists=true
。此参数可以与facet.method=enum
一起使用,也可以在省略时使用。它只能用于非 trie 字段(例如字符串)。它可以加速大型索引和/或高基数分面值的分面计数。 facet.excludeTerms
-
可选
默认值:无
从分面计数中删除词项,但将其保留在索引中。
facet.overrequest.count
和facet.overrequest.ratio
-
可选
默认值:请参阅描述
在某些情况下,通过从每个单独的分片“过度请求”所需的约束数量(即
facet.limit
),可以提高在分布式 Solr 查询中选择分面返回的“顶部”约束的准确性。在这些情况下,默认情况下会要求每个分片提供前10 + (1.5 * facet.limit)
个约束。根据文档在分片上的分区方式以及使用的
facet.limit
值,您可能会发现增加或减少 Solr 执行的过度请求量是有利的。可以通过设置facet.overrequest.count
(默认为10
)和facet.overrequest.ratio
(默认为1.5
)参数来实现。 facet.threads
-
可选
默认值:0
用于加载分面中使用的基础字段的最大并行线程数。
省略此参数或将线程计数指定为
0
将不会生成任何线程,并且只会使用主请求线程。指定负数的线程将最多创建Integer.MAX_VALUE
个线程。
范围分面
可以在任何日期字段或任何支持范围查询的数字字段上使用范围分面。这对于将一系列范围查询(作为按查询分面)拼接在一起以用于价格等内容特别有用。
facet.range
-
必需
默认值:无
Solr 应为其创建范围分面的字段。例如
facet.range=price&facet.range=age
facet.range=lastModified_dt
facet.range.start
-
必需
默认值:无
范围的下限。可以使用
f.<fieldname>.facet.range.start
的语法按字段指定此参数。例如f.price.facet.range.start=0.0&f.age.facet.range.start=10
f.lastModified_dt.facet.range.start=NOW/DAY-30DAYS
facet.range.end
-
必需
默认值:无
范围的上限。可以使用
f.<fieldname>.facet.range.end
的语法按字段指定此参数。例如f.price.facet.range.end=1000.0&f.age.facet.range.start=99
f.lastModified_dt.facet.range.end=NOW/DAY+30DAYS
facet.range.gap
-
必需
默认值:无
每个范围的跨度,表示为要添加到下限的值。对于日期字段,应使用
DateMathParser
语法(例如,facet.range.gap=%2B1DAY … '+1DAY'
)。可以使用
f.<fieldname>.facet.range.gap
的语法按字段指定此参数。例如f.price.facet.range.gap=100&f.age.facet.range.gap=10
f.lastModified_dt.facet.range.gap=+1DAY
facet.range.hardend
-
可选
默认值:
false
如何处理
facet.range.gap
不能在facet.range.start
和facet.range.end
之间均匀分割的情况。如果为
true
,则最后一个范围约束的上限将为facet.range.end
值。如果为false
,则最后一个范围将具有大于facet.range.end
的最小可能上限,以便该范围与指定的范围间隙的宽度完全相同。可以使用
f.<fieldname>.facet.range.hardend
的语法按字段指定此参数。 facet.range.include
-
可选
默认值:请参阅描述
默认情况下,用于计算
facet.range.start
和facet.range.end
之间的范围分面的范围包含其下限,而不包含上限。使用facet.range.other
参数定义的“之前”范围是排他的,“之后”范围是包含的。此默认值(相当于下面的“lower”)不会导致边界处重复计数。可以使用facet.range.include
参数,使用以下选项修改此行为-
lower
:所有基于间隙的范围都包含其下限。 -
upper
:所有基于间隙的范围都包含其上限。 -
edge
:第一个和最后一个间隙范围包含其边缘边界(第一个范围的下限,最后一个范围的上限),即使未指定相应的 upper/lower 选项。 -
outer
:“之前”和“之后”范围将包含其边界,即使第一个或最后一个范围已经包含这些边界。 -
all
:包括所有选项:lower
、upper
、edge
和outer
。
可以使用
f.<fieldname>.facet.range.include
的语法按字段指定此参数,并且可以多次指定以指示多个选择。 -
为了确保避免重复计数,请勿同时选择 lower 和 upper ,请勿选择 outer ,并且不要选择 all 。 |
facet.range.other
-
可选
默认值:无
除了
facet.range.start
和facet.range.end
之间每个范围约束的计数外,还会计算以下选项的计数-
before
:所有字段值低于第一个范围的下限的记录。 -
after
:所有字段值大于最后一个范围的上限的记录。 -
between
:所有字段值介于所有范围的开始和结束边界之间的记录。 -
none
:不计算任何计数。 -
all
:计算之前、之间和之后的计数。
可以使用
f.<fieldname>.facet.range.other
的语法按字段指定此参数。除了all
选项外,可以多次指定此参数以指示多个选择,但none
将覆盖所有其他选项。 -
facet.range.method
-
可选
默认值:
filter
选择用于范围分面的算法或方法类型。两种方法都会产生相同的结果,但性能可能会有所不同。
- filter
-
根据其他 facet.range 参数生成范围,并为每个范围执行一个过滤器,该过滤器稍后与主查询结果集相交以获取计数。它将使用 filterCache,因此它将受益于足够大的缓存以包含所有范围。
- dv
-
迭代匹配主查询的文档,并为每个文档查找该值的正确范围。此方法将使用 DocValues(如果为该字段启用)或 fieldCache。字段类型为 DateRangeField 或使用 group.facets 时不支持
dv
方法。
数据透视表(决策树)分面
数据透视表是一种汇总工具,可让您自动排序、计数、汇总或平均存储在表格中的数据。结果通常显示在第二个表格中,其中显示汇总的数据。数据透视表分面可让您创建通过多个字段分面文档的结果的摘要表。
另一种看待它的方式是,查询会生成一个决策树。Solr 会告诉你:“对于 facet A,约束/计数是 X/N, Y/M 等。如果你通过 X 约束 A,那么 B 的约束计数将是 S/P, T/Q 等。”换句话说,它会提前告诉你,如果你应用当前 facet 结果的约束,字段的“下一个” facet 结果集会是什么。
facet.pivot
-
可选
默认值:无
用于 pivot 的字段。多个
facet.pivot
值将在响应中创建多个 “facet_pivot” 部分。用逗号分隔每个字段列表。 facet.pivot.mincount
-
可选
默认值:
1
为了使 facet 包含在结果中,需要匹配的最小文档数。
使用 “bin/solr start -e techproducts” 示例,像这样的查询 URL 将返回以下数据,其中 pivot 分面结果位于 “facet_pivot” 部分。
https://127.0.0.1:8983/solr/techproducts/select?q=*:*&facet.pivot=cat,popularity,inStock &facet.pivot=popularity,cat&facet=true&facet.field=cat&facet.limit=5&rows=0&facet.pivot.mincount=2
{ "facet_counts":{ "facet_queries":{}, "facet_fields":{ "cat":[ "electronics",14, "currency",4, "memory",3, "connector",2, "graphics card",2]}, "facet_dates":{}, "facet_ranges":{}, "facet_pivot":{ "cat,popularity,inStock":[{ "field":"cat", "value":"electronics", "count":14, "pivot":[{ "field":"popularity", "value":6, "count":5, "pivot":[{ "field":"inStock", "value":true, "count":5}]}] }]}}}
将统计组件与 Pivots 结合使用
除了其他类型的分面支持的一些通用本地参数之外,stats
本地参数可以与 facet.pivot
一起使用,以引用您希望为每个 Pivot 约束计算的stats.field
实例(通过标记)。
在下面的示例中,为每个 facet.pivot 结果层次结构计算两个不同的(重叠的)统计数据集。
stats=true
stats.field={!tag=piv1,piv2 min=true max=true}price
stats.field={!tag=piv2 mean=true}popularity
facet=true
facet.pivot={!stats=piv1}cat,inStock
facet.pivot={!stats=piv2}manu,inStock
结果
{"facet_pivot":{
"cat,inStock":[{
"field":"cat",
"value":"electronics",
"count":12,
"pivot":[{
"field":"inStock",
"value":true,
"count":8,
"stats":{
"stats_fields":{
"price":{
"min":74.98999786376953,
"max":399.0}}}},
{
"field":"inStock",
"value":false,
"count":4,
"stats":{
"stats_fields":{
"price":{
"min":11.5,
"max":649.989990234375}}}}],
"stats":{
"stats_fields":{
"price":{
"min":11.5,
"max":649.989990234375}}}},
{
"field":"cat",
"value":"currency",
"count":4,
"pivot":[{
"field":"inStock",
"value":true,
"count":4,
"stats":{
"stats_fields":{
"price":{
"..."
"manu,inStock":[{
"field":"manu",
"value":"inc",
"count":8,
"pivot":[{
"field":"inStock",
"value":true,
"count":7,
"stats":{
"stats_fields":{
"price":{
"min":74.98999786376953,
"max":2199.0},
"popularity":{
"mean":5.857142857142857}}}},
{
"field":"inStock",
"value":false,
"count":1,
"stats":{
"stats_fields":{
"price":{
"min":479.95001220703125,
"max":479.95001220703125},
"popularity":{
"mean":7.0}}}}],
"..."}]}}}}]}]}}
将 Facet 查询和 Facet 范围与 Pivot Facets 结合使用
query
本地参数可以与 facet.pivot
一起使用,以引用应为每个 pivot 约束计算的 facet.query
实例(通过标记)。类似地,range
本地参数可以与 facet.pivot
一起使用,以引用 facet.range
实例。
在下面的示例中,为 h 的 facet.pivot
结果层次结构计算两个查询分面。
facet=true
facet.query={!tag=q1}manufacturedate_dt:[2006-01-01T00:00:00Z TO NOW]
facet.query={!tag=q1}price:[0 TO 100]
facet.pivot={!query=q1}cat,inStock
{"facet_counts": {
"facet_queries": {
"{!tag=q1}manufacturedate_dt:[2006-01-01T00:00:00Z TO NOW]": 9,
"{!tag=q1}price:[0 TO 100]": 7
},
"facet_fields": {},
"facet_dates": {},
"facet_ranges": {},
"facet_intervals": {},
"facet_heatmaps": {},
"facet_pivot": {
"cat,inStock": [
{
"field": "cat",
"value": "electronics",
"count": 12,
"queries": {
"{!tag=q1}manufacturedate_dt:[2006-01-01T00:00:00Z TO NOW]": 9,
"{!tag=q1}price:[0 TO 100]": 4
},
"pivot": [
{
"field": "inStock",
"value": true,
"count": 8,
"queries": {
"{!tag=q1}manufacturedate_dt:[2006-01-01T00:00:00Z TO NOW]": 6,
"{!tag=q1}price:[0 TO 100]": 2
}
},
"..."]}]}}}
类似地,在下面的示例中,为每个 facet.pivot
结果层次结构计算两个范围分面。
facet=true
facet.range={!tag=r1}manufacturedate_dt
facet.range.start=2006-01-01T00:00:00Z
facet.range.end=NOW/YEAR
facet.range.gap=+1YEAR
facet.pivot={!range=r1}cat,inStock
{"facet_counts":{
"facet_queries":{},
"facet_fields":{},
"facet_dates":{},
"facet_ranges":{
"manufacturedate_dt":{
"counts":[
"2006-01-01T00:00:00Z",9,
"2007-01-01T00:00:00Z",0,
"2008-01-01T00:00:00Z",0,
"2009-01-01T00:00:00Z",0,
"2010-01-01T00:00:00Z",0,
"2011-01-01T00:00:00Z",0,
"2012-01-01T00:00:00Z",0,
"2013-01-01T00:00:00Z",0,
"2014-01-01T00:00:00Z",0],
"gap":"+1YEAR",
"start":"2006-01-01T00:00:00Z",
"end":"2015-01-01T00:00:00Z"}},
"facet_intervals":{},
"facet_heatmaps":{},
"facet_pivot":{
"cat,inStock":[{
"field":"cat",
"value":"electronics",
"count":12,
"ranges":{
"manufacturedate_dt":{
"counts":[
"2006-01-01T00:00:00Z",9,
"2007-01-01T00:00:00Z",0,
"2008-01-01T00:00:00Z",0,
"2009-01-01T00:00:00Z",0,
"2010-01-01T00:00:00Z",0,
"2011-01-01T00:00:00Z",0,
"2012-01-01T00:00:00Z",0,
"2013-01-01T00:00:00Z",0,
"2014-01-01T00:00:00Z",0],
"gap":"+1YEAR",
"start":"2006-01-01T00:00:00Z",
"end":"2015-01-01T00:00:00Z"}},
"pivot":[{
"field":"inStock",
"value":true,
"count":8,
"ranges":{
"manufacturedate_dt":{
"counts":[
"2006-01-01T00:00:00Z",6,
"2007-01-01T00:00:00Z",0,
"2008-01-01T00:00:00Z",0,
"2009-01-01T00:00:00Z",0,
"2010-01-01T00:00:00Z",0,
"2011-01-01T00:00:00Z",0,
"2012-01-01T00:00:00Z",0,
"2013-01-01T00:00:00Z",0,
"2014-01-01T00:00:00Z",0],
"gap":"+1YEAR",
"start":"2006-01-01T00:00:00Z",
"end":"2015-01-01T00:00:00Z"}}},
"..."]}]}}}
区间分面
另一种受支持的分面形式是区间分面。这听起来类似于范围分面,但其功能实际上更接近于使用范围查询进行分面查询。区间分面允许你设置可变区间,并计算指定字段中值在这些区间内的文档数量。
即使使用带有范围查询的分面查询可以实现相同的功能,但这两种方法的实现方式非常不同,并且会根据上下文提供不同的性能。
如果你担心搜索的性能,则应测试这两种选项。区间分面在同一字段的多个区间上往往更好,而分面查询在过滤器缓存更有效的环境(例如静态索引)中往往更好。
如果为该字段启用了 DocValues,此方法将使用它;否则将使用 fieldCache。
将这些参数用于区间分面
facet.interval
-
可选
默认值:无
必须应用区间分面的字段。它可以在同一请求中多次使用,以指示多个字段。
facet.interval=price&facet.interval=size
facet.interval.set
-
可选
默认值:无
设置字段的区间。可以多次指定它以指示多个区间。此参数是全局的,这意味着它将用于所有使用
facet.interval
指示的字段,除非特定字段有覆盖。要覆盖特定字段上的此参数,可以使用:f.<fieldname>.facet.interval.set
,例如f.price.facet.interval.set=[0,10]&f.price.facet.interval.set=(10,100]
区间语法
区间必须以 '(' 或 '[' 开头,后跟起始值,然后是逗号 (','),结束值,最后是结束 ')' 或 ']'。
例如
-
(1,10) → 将包括大于 1 且小于 10 的值
-
[1,10) → 将包括大于或等于 1 且小于 10 的值
-
[1,10] → 将包括大于或等于 1 且小于或等于 10 的值
初始值和结束值不能为空。
如果区间需要无界限,则特殊字符 *
可用于开始和结束限制。使用此特殊字符时,开始语法选项 ((
和 [
) 以及结束语法选项 ()
和 ]
) 将被视为相同。[*,*]
将包括字段中具有值的所有文档。
区间限制可以是字符串,但无需添加引号。逗号之前的所有文本将被视为开始限制,之后的文本将被视为结束限制。例如:[Buenos Aires,New York]
。请记住,将进行类似字符串的比较以匹配字符串区间中的文档(区分大小写)。比较器无法更改。
逗号、括号和方括号可以通过在其前面使用 \
来转义。值之前和之后的空格将被省略。
开始限制不能大于结束限制。允许相等限制,这允许你指示要计数的特定值,例如 [A,A]
,[B,B]
和 [C,Z]
。
区间分面支持下面描述的输出键替换。可以在 facet.interval
参数和 facet.interval.set
参数中替换输出键。例如
&facet.interval={!key=popularity}some_field
&facet.interval.set={!key=bad}[0,5]
&facet.interval.set={!key=good}[5,*]
&facet=true
用于分面的本地参数
LocalParams 语法 允许覆盖全局设置。它还可以提供一种向其他参数值添加元数据的方法,很像 XML 属性。
标记和排除过滤器
你可以标记特定的过滤器,并在分面时排除这些过滤器。这在进行多选分面时很有用。
考虑以下带有分面的示例查询
q=mainquery&fq=status:public&fq=doctype:pdf&facet=true&facet.field=doctype
因为所有内容都已受到过滤器 doctype:pdf
的约束,所以 facet.field=doctype
分面命令当前是多余的,并且除了 doctype:pdf
之外,所有内容都将返回 0 计数。
要为 doctype 实现多选分面,GUI 可能仍然希望显示其他 doctype 值及其关联的计数,就像尚未应用 doctype:pdf
约束一样。例如
=== Document Type ===
[ ] Word (42)
[x] PDF (96)
[ ] Excel(11)
[ ] HTML (63)
要返回当前未选择的 doctype 值的计数,请标记直接约束 doctype 的过滤器,并在分面 doctype 时排除这些过滤器。
q=mainquery&fq=status:public&fq={!tag=dt}doctype:pdf&facet=true&facet.field={!ex=dt}doctype
所有类型的分面都支持过滤器排除。tag
和 ex
本地参数都可以通过用逗号分隔指定多个值。
更改输出键
要更改分面命令的输出键,请使用 key
本地参数指定新名称。例如
facet.field={!ex=dt key=mylabel}doctype
上面的参数设置会导致将 “doctype” 字段的字段分面结果返回,在响应中使用键 “mylabel” 而不是 “doctype”。当使用不同的排除多次在同一字段上进行分面时,这可能会有所帮助。
使用特定词项限制分面
要使用特定词项限制字段分面,请使用 terms
本地参数用逗号分隔指定它们。可以使用反斜杠转义词项中的逗号和引号,例如 \,
。在这种情况下,分面的计算方式类似于 facet.method=enum
,但会忽略 facet.enum.cache.minDf
。例如
facet.field={!terms='alfa,betta,with\,with\',with space'}symbol
此本地参数会覆盖 facet.sort
的默认逻辑。如果省略 facet.sort
,则分面将按给定的词项顺序返回,该顺序可能会通过 index
和 count
值更改。注意:当提供此参数时,可能不完全支持其他参数。