solr 字段类型
字段类型定义了 solr 如何解释字段的数据及字段如何被查询。solr 默认包含了很多字段类型,而且也可以自定义。
定义和属性
字段类型的定义可包含 4 类信息
- 字段名(必须)
- 实现类名(必须)
- 如果字段类型是
TextField
,字段解析的说明 - 字段类型属性 - 依赖于实现类,有些属性可能是必须的
schema.xml 里的字段定义
字段类型在 schema.xml
里定义。每个字段类型定义在 fieldType
元素之间。它们可以用 types
元素集中。下面是一个字段类型 text_general
定义的例子
<fieldType name="text_general" class="solr.TextField" positionIncrementGap="100">
<analyzer type="index">
<tokenizer class="solr.StandardTokenizerFactory"/>
<filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt"/>
<!-- in this example, we will only use synonyms at query time
<filter class="solr.SynonymFilterFactory" synonyms="index_synonyms.txt" ignoreCase="true" expand="false"/>
-->
<filter class="solr.LowerCaseFilterFactory"/>
</analyzer>
<analyzer type="query">
<tokenizer class="solr.StandardTokenizerFactory"/>
<filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt"/>
<filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt"
ignoreCase="true" expand="true"/>
<filter class="solr.LowerCaseFilterFactory"/>
</analyzer>
</fieldType>
上面例子里的第一行包含了字段类型的名字,text_general
,以及实现类的名字,solr.TextField
。其余的定义是关于字段解析,参考 Understanding Analyzers, Tokenizers, and Filters
实现类确保字段被正确处理。在 schema.xml
里的类名,字符串 solr
是 org.apache.solr.schema
或 org.apache.solr.analysis
的简写。因此,solr.TextField
实际是 org.apache.solr.schema.TextField
。
字段类型属性
字段类型的 class
决定了字段类型大多数行为,但是仍然可以定义可选的属性。例如,下面的日期字段类型定义了 2 个属性,sortMissingLast
和 omitNorms
<fieldType name="date" class="solr.TrieDateField" sortMissingLast="true" omitNorms="true"/>
字段类型分为 3 个大类
- 字段类型的类
- 通用属性 所有字段类型都支持
- 字段默认属性 在字段类型上指定后,可以被字段继承替换字段默认行为的属性(存疑:如果字段上定义的属性和字段类型不同,应该是以字段上定义的为准)
通用属性
属性 | 描述 | 值 |
---|---|---|
name | 字段类型名字。这个值在字段定义时用于 "type" 属性。强烈建议名字以字母或下划线而非数字开头。目前并非强制要求 | |
class | 存储和索引该类型数据的类。注意,类名可以用 "solr" 前缀,solr 会自动识别该类在哪个包 - 所以 "solr.TextField" 有效。如果使用第三方的类,使用完整的类名。"solr.TextField" 等效于 "org.apache.solr.schema.TextField"" | |
positionIncrementGap | 对于多值字段,指定多值之间的距离,这能避免"虚假"的短语匹配 | integer |
autoGeneratePhraseQueries | 用于文本字段。如果为 true,solr 自动对相邻的词条生成短语查询。如为 false,只有用双引号包围的词条才会作为短语处理 | true 或 false |
docValuesFormat | 定义一个自定义 DocValuesFormat 的类型字段 ,需要一个 schema 感知的编解码器,诸如 SchemaCodecFactory 已在 solrconfig.xml 里配置好 |
n/a |
postingsFormat | 定义一个自定义 PostingsFormat 的类型字段 ,需要一个 schema 感知的编解码器,诸如 SchemaCodecFactory 已在 solrconfig.xml 里配置好 |
n/a |
Lucene 索引向后兼容只支持默认的编解码器。如果在 schema.xml 里自定义
postingsFormat
或docValuesFormat
,升级到新版本的 solr 之前可能要求你切换到默认的编解码器并优化索引以重新写入默认编解码器,或者在升级之后从零开始重建索引。
字段默认属性
这些属性要么在字段类型指定,要么在特定字段上覆盖字段类型的指定。每个属性的默认值依赖于对应的 FieldType
类,取决于 <schema/>
的 version
属性值。下面的表格包含了 solr 提供的大多数 FieldType
实现的默认值,如果 schema.xml
的 version="1.6"
属性 | 描述 | 值 | 隐含默认值 |
---|---|---|---|
indexed | 如为 true,字段值能用于查询检索匹配的文档 | true/false | true |
stored | 如为 true,字段值可以被查询获取 | true/false | true |
docValues | 如为 true,字段值将放入面向列的 DocValues 结构 |
true/false | false |
sortMissingFirst sortMissingLast |
当一个索引字段未提供时,控制文档(在索引)的位置(是在最前/最后) | true/false | false |
multiValued | 如为 true,单个文档在这个字段可能包含多个值 | true/false | false |
omitNorms | 如为 true,省略与此字段关联的加权基准(这会关闭长度标准化,及索引时对该字段的加权,从而节省内存占用)。对所有原始(无需解析)的字段类型,诸如 int,float,date,bool,和 string,默认为 true。只有全文本字段或需要索引时加权的字段才需要加权基准(norms) | true/false | * |
omitTermFreqAndPositions | 如为 true,在提交字段时省略词条的频率/次数,位置,载荷。对于不需要这些信息的字段能提升性能,同时还减少了索引需要的存储空间。依赖位置信息的查询会无声的失败。对于所有非文本字段这个属性默认为 true | true/false | * |
omitPositions | 和 omitTermFreqAndPositions 类似,但是会保留词条频率/次数信息 |
true/false | * |
termVectors termPositions termOffsets termPayloads |
这些选项命令 solr 保持每个文档完整的词条向量,随意的包括在那些向量里每个词条的位置,偏移,及载荷信息。这些可用于加快高亮和其他辅助功能,但会极大增加索引的尺寸。在 solr 的典型应用里是没有必要的 | true/false | false |
required | 如为 true,solr 拒绝添加一个该字段没有值的文档 | true/false | false |
useDocValuesAsStored | 如果该字段开启了 docValues ,设为 true 将允许该字段在 fl=* 时像一个存储的字段一样返回(即使该字段的 stored=false ) |
true/false | true |
term 的
Vector
实际上就是由 term 的positions
,offset
,payloads
组成的
字段类型相似性
字段类型可以指定 <similarity/>
,可用于当给一个有该字段类型字段的文档评分时,只要 collection 的 "global" 相似性允许。默认情况,所有没有定义 similarity 的字段类型使用 BM25Similarity
。要了解更多细节,及配置 global 和 每个类型的 similarity,参考 Other Schema Elements
solr 自带字段类型
下面表格列出了 solr 可用的字段类型。org.apache.solr.schema
包包含列出的所有类
类 | 描述 |
---|---|
BinaryField | 二进制数据 |
BoolField | 包含 true 或 false。"1", "t", "T" 为首字母视同 true,其他首字母的视同 false |
CollationField | 索引和范围查询支持 Unicode 排序规则。如果使用 ICU4J,ICUCollationField 是个更好的选择。参考 Unicode Collation |
CurrencyField | 支持货币和汇率 |
DateRangeField | 支持日期范围索引,也包括时间点 |
ExternalFileField | 从磁盘文件里拉取数值 |
EnumField | 对于那些不容易以字母或数字顺序来保存的(例如一个严重程度的列表),可定义一个值的枚举集。这个字段类型需要一个配置文件,用来有序的记录字段值 |
ICUCollationField | 索引和范围查询支持 Unicode 排序规则。参考 Unicode Collation |
LatLonType | 相联系的纬度,经度对,纬度在前 |
PointType | 任意维度的点,在蓝图或CAD绘图等资源里搜索时有用 |
PreAnalyzedField | 提供了一种向 solr 发送连续的词元流的方法,词元流可附带独立的存储字段,并将此信息存储和索引而不做任何额外的处理 |
RandomSortField | 该字段类型不包含值(?)查询如果在这个字段上做排序将随机排列。使用动态字段来使用这个特性 |
SpatialRecursivePrefixTreeFieldType | (简写为 RPT) 维度+逗号+经度字符串或其他的 WKT 格式 |
StrField | 字符串(UTF8 编码或 Unicode) |
TextField | 文本,通常是多个单词或词元 |
TrieDateField | 日期precisionStep="0" 可按日期排序,索引最小precisionStep="8" 默认值,可范围查询 |
TrieDoubleField | double,8字节precisionStep="0" 可按数字排序,索引最小precisionStep="8" 默认值,可范围查询 |
TrieField | 这个类型必须指定 type 属性,可选的值为 integer , long , float , double , date 。等效于对应的 Trie 字段precisionStep="0" 可按数字排序,索引最小precisionStep="8" 默认值,可范围查询 |
TrieFloatField | float,4字节precisionStep="0" 可按数字排序,索引最小precisionStep="8" 默认值,可范围查询 |
TrieIntField | int,4字节precisionStep="0" 可按数字排序,索引最小precisionStep="8" 默认值,可范围查询 |
TrieLongField | long,8字节precisionStep="0" 可按数字排序,索引最小precisionStep="8" 默认值,可范围查询 |
UUIDField | 通用唯一标识符,传入 "NEW",solr 会创建一个 UUID。注意:在 SolrCloud 模式,对于大多数用户配置一个 UUIDField 默认值为 "NEW" 是不明智的(而且,如果 UUID 值配置为唯一键,也不可能),因为这会导致每个 replica 的每个 document 都有一个唯一的 UUID 值。建议用 UUIDUpdateProcessorFactory 来生成 UUID 值 |
货币和汇率
Currency
字段类型支持以下特性
- 点查询
- 范围查询
- 函数范围查询
- 排序
- 使用货币代码或者符号解析货币
- 对称和非对称汇率
货币配置
Currency
字段类型在 schema.xml
定义,默认配置如下
<fieldType name="currency" class="solr.CurrencyField" precisionStep="8"
defaultCurrency="USD" currencyConfig="currency.xml" />
这个例子里,我们定义了字段类型的名字和类,定义了 defaultCurrency
为 "USD",即美元。还定义了 currencyConfig
,使用名为 currency.xml
的文件。这个是默认货币到其他货币的汇率文件。还有一个代替的实现允许下载货币数据
索引期间,金额字段能被索引为本地货币。例如,一个欧洲电子商务网站上的商品,索引其价格字段为 "100,EUR" 是很合理的。价格和货币之间用逗号分隔,价格应该被编码为一个浮点值。
查询时,范围和点查询都可以支持。
汇率
通过指定提供者来配置汇率。Solr 自带 2 类提供者:FileExchangeRateProvider
或 OpenExchangeRatesOrgProvider
FileExchangeRateProvider
这个提供者需要你提供一个汇率文件。这是默认的,意思是要用这个提供者你只需要指定文件的路径和名字到 currencyConfig
这里有一个 solr 自带的示例 currency.xml
文件,和 shema.xml
文件在同一个目录,下面是该文件的一小段
<currencyConfig version="1.0">
<rates>
<!-- Updated from http://www.exchangerate.com/ at 2011-09-27 -->
<rate from="USD" to="ARS" rate="4.333871" comment="ARGENTINA Peso" />
<rate from="USD" to="AUD" rate="1.025768" comment="AUSTRALIA Dollar" />
<rate from="USD" to="EUR" rate="0.743676" comment="European Euro" />
<rate from="USD" to="CAD" rate="1.030815" comment="CANADA Dollar" />
<!-- Cross-rates for some common currencies -->
<rate from="EUR" to="GBP" rate="0.869914" />
<rate from="EUR" to="NOK" rate="7.800095" />
<rate from="GBP" to="NOK" rate="8.966508" />
<!-- Asymmetrical rates -->
<rate from="EUR" to="USD" rate="0.5" />
</rates>
</currencyConfig>
OpenExchangeRatesOrgProvider
你可以配置 solr 从 OpenExchangeRates.org 下载汇率,可以每小时更新美元和 170 种货币的汇率。
这个案例里,你需要指定 providerClass
,并注册以获得一个 API key,如下所示
<fieldType
name="currency"
class="solr.CurrencyField"
precisionStep="8"
providerClass="solr.OpenExchangeRatesOrgProvider"
refreshInterval="60"
ratesFileLocation="http://www.openexchangerates.org/api/latest.json?app_id=yourPersonalAppIdKey"/>
refreshInterval
以分钟为单位,所以上面例子会每隔 60 分钟下载最新的汇率。这个刷新间隔可以加大,但不能减少。
日期
日期格式化
solr 的 日期字段 (TrieDateField
和 DateRangeField
) 表现为一个毫秒精度的时间点。其所用格式是 XML Schema specification(ISO-8601 的一个有限子集)的 dateTime
的权威表述的有限格式(这一段大概意思就是并不支持各种日期格式,只支持有限的一种或几种)。如果熟悉 Java 8,solr 使用 DateTimeFormatter.ISO_INSTANT 来格式化和解析。
YYYY-MM-DDThh:mm:ssZ
YYYY
年MM
月DD
月份里的日期hh
24小时制的小时mm
分钟ss
秒Z
就是字符 'Z',表示这个字符串用 UTC 表示日期
注意,不能指定时区,表示时间的字符串总是以 UTC 来表示,示例如下
1972-05-20T17:33:18Z
如果你愿意,还可以包含秒的小数,但是任何超出毫秒的精度都会被忽略。示例如下
1972-05-20T17:33:18.772Z
1972-05-20T17:33:18.77Z
1972-05-20T17:33:18.7Z
在 0000 年之前的日期,必须以 '-'
开头,'+'
开头表示 9999 年以后。0000 年 视为 公元前 1 年(year 1 BC),没有 公元前/后 0 年这种事。
查询时可能要转义
如你所见,日期格式包含了冒号来分隔时,分和秒。由于对于 solr 大多数的查询解析器来说,冒号是个特殊字符,所以有时候需要转义
这是个无效查询
datefield:1972-05-20T17:33:18.772Z
这些是有效查询
datefield:1972-05-20T17\:33\:18.772Z
datefield:"1972-05-20T17:33:18.772Z"
datefield:[1972-05-20T17:33:18.772 TO *]
日期范围格式化
solr 的 DateRangeField
支持前面所述的时刻语法(和下面要讲的数学日期一起)和日期范围表示。一个例子是截断日期,表示整个日期跨度。其他例子是范围语法([ TO ]
),这里有些例子
2000-11
– 2000 年 整个 11 月2000-11T13
– 同上,不过是当日 13 时(下午 1-2 时)-0009
– 公元前 10 年。0 年,是公元 0 年,也是公元前 1 年[2000-11-01 TO 2014-12-01]
– 不解释(霸气~)[2014 TO 2014-12-01]
– 从 2014 到 2014-12-01[* TO 2014-12-01]
– 从可表示的最早时间到 2014-12-01
局限:范围语法不支持内嵌数学日期。如果你指定一个用数学日期截断的 TrieDateField
日期实例,如 NOW/DAY
,你得到的依然是当天的第一毫秒,而不是整天的范围。开区间范围(使用 {
和 }
)在查询时可用,但不能用来做范围索引。
数学日期
solr 日期字段类型也支持数学日期表达式,这使得创建一个相对于某确定时刻的时间更容易了,包括用特殊的值 "NOW
" 表示当前时间。
数学日期语法
数学日期表达式不是以特定单位增加时间,就是以特定单位对当前时间取整。表达式可以是链式的,从左至右执行
例如:从现在到 2 个月后的时刻
NOW+2MONTHS
一天以前
NOW-1DAY
斜杠表示取整,下面表示当前小时的开始
NOW/HOUR
下面例子计算(毫秒精度) 6 个月 3 天后并按天取整,即那天开始的时刻
NOW+6MONTHS+3DAYS/DAY
注意,数学日期最常和 NOW 一起用,与此同时,也可以和任何确定的时刻一起
1972-05-20T17:33:18.772Z+6MONTHS+3DAYS/DAY
影响数学日期的请求参数
NOW
在一个分布式请求里,solr 内部使用 NOW
参数来确保数学日期表达式跨越多个节点解析的一致性。但是,它也可以被一个任意的时刻(过去或未来)覆盖。
如果要设定 NOW
的值,必须为从 epoch(1970年1月1日00:00:00 UTC) 开始的毫秒数
示例
q=solr&fq=start_date:[* TO NOW]&NOW=1384387200000
TZ
默认情况下,所有数学日期表达式都被认为是相对于 UTC 时区,但是,可以设定 TZ
参数来覆盖默认行为。
例如,下面的请求在 UTC 当前月的每一天使用范围 faceting
http://localhost:8983/solr/my_collection/select?\
q=*:*&facet.range=my_date_field&facet=true&facet.range.start=NOW/MONTH&\
facet.range.end=NOW/MONTH%2B1MONTH&facet.range.gap=%2B1DAY
<int name="2013-11-01T00:00:00Z">0</int>
<int name="2013-11-02T00:00:00Z">0</int>
<int name="2013-11-03T00:00:00Z">0</int>
<int name="2013-11-04T00:00:00Z">0</int>
<int name="2013-11-05T00:00:00Z">0</int>
<int name="2013-11-06T00:00:00Z">0</int>
<int name="2013-11-07T00:00:00Z">0</int>
...
下面这个例子,使用指定的时区
http://localhost:8983/solr/my_collection/select?\
q=*:*&facet.range=my_date_field&facet=true&facet.range.start=NOW/MONTH&\
facet.range.end=NOW/MONTH%2B1MONTH&facet.range.gap=%2B1DAY&TZ=America/Los_Angeles
<int name="2013-11-01T07:00:00Z">0</int>
<int name="2013-11-02T07:00:00Z">0</int>
<int name="2013-11-03T07:00:00Z">0</int>
<int name="2013-11-04T08:00:00Z">0</int>
<int name="2013-11-05T08:00:00Z">0</int>
<int name="2013-11-06T08:00:00Z">0</int>
<int name="2013-11-07T08:00:00Z">0</int>
...
更多 DataRangeField 细节
几乎所有使用 TrieDateField
的地方都可以随时用 DateRangeFiled
替换。唯一的不同是 solr 的 xml 或 solrj 里显示其存储的数据格式为字符串而不是日期型。索引的数据会大一点。按时间单位秒对齐的查询要比 TrieDateField 更快,尤其是用 UTC。但主要的是,正如其名,允许按日期范围索引。为此,只需要按上面格式提交字符串。它还支持在索引数据和查询范围间的 3 种不同的断言:Intersects
(默认),Contains
,Within
。可以在查询里用本地参数 op
来指定断言,如下
fq={!field f=dateRange op=Contains}[2013 TO 2018]
这个例子里,会查找索引范围包含(或等于) 2013 到 2018 的文档。
DateRangeFiled 示例和其他信息,参阅 solr's community wiki
枚举字段
枚举字段类型可以定义一个取值范围是一个闭集的字段,且值的排序顺序是预定义好的,而非按字母或数字排序。例如,严重性列表,风险列表。
在 schema.xml 里定义枚举字段
枚举字段类型的定义很简单,如下面的例子,定义了严重级别和风险级别的枚举
<fieldType name="priorityLevel" class="solr.EnumField"
enumsConfig="enumsConfig.xml" enumName="priority"/>
<fieldType name="riskLevel" class="solr.EnumField"
enumsConfig="enumsConfig.xml" enumName="risk" />
除了 name
和 class
,这个类型还有 2 个额外的参数
enumsConfig
:包含了该字段类型要用到的<enum/>
字段值列表及其顺序的配置文件名。如果没有指定路径,这个文件应该在 collection 的conf
目录enumName
:枚举的名字,在enumsConfig
文件里要用。
枚举字段配置文件
enumConfig
参数指定的文件,可以包含多个枚举值列表,各有不同的名字,可用于你的 solr schema 有多个枚举字段类型。
这个例子里,定义了 2 个枚举值列表,每个都在 enum
标签内
<?xml version="1.0" ?>
<enumsConfig>
<enum name="priority">
<value>Not Available</value>
<value>Low</value>
<value>Medium</value>
<value>High</value>
<value>Urgent</value>
</enum>
<enum name="risk">
<value>Unknown</value>
<value>Very Low</value>
<value>Low</value>
<value>Medium</value>
<value>High</value>
<value>Critical</value>
</enum>
</enumsConfig>
修改枚举值
除非重建索引,不能修改枚举值的顺序,或移除在
<enum/>
里的值,也不能在末尾添加新的值。
外部文件及处理
字段属性用例
这是一个公共用例的概要,以及字段或字段类型要支持该用例所需要的属性。表格里的 true 或 false 表示为了功能正确选项必须设为给定的值。如果输入未提供,则属性值对用例没有影响。
用例 | indexed | stored | multiValued | omitNorms | termVectors | termPositions | docValues |
---|---|---|---|---|---|---|---|
search within field | true | ||||||
retrieve contents | true | ||||||
use as unique key | true | false | |||||
sort on field | true(7) | false | true(1) | true(7) | |||
use field boosts | false | ||||||
document boosts affect searches within field | false | ||||||
highlighting | true(4) | true | true(2) | true(3) | |||
facting(5) | true(7) | true(7) | |||||
add multiple values,maintaining order | true | ||||||
field length affects doc score | false | ||||||
MoreLikeThis | true(6) |
注意
- 推荐,但不是必须的
- 如果提供了就会使用,非必须
- if termVectors=true
- 该字段必须定义 tokenizer,但不必是 indexed 的
- faceting,参考
Understanding Analyzers, Tokenizers, and Filters
- 这里 Term vectors 不是强制的。如果不是 true,那么一个 stored 字段被分析。所以,term vector 是建议的,但仅当 stored=false 时才是必须的
- indexed 和 docValues 都必须是 true,但都不是必须的。DocValues 在很多场景更有效