schema.xml用来定义文档的结构。一个文档有一个或多个域组成,而域又需要由不同的域类型进行定义。并且在创建索引的过程中,solr又需要对域进行分词。以下讲一一对以上内容进行说明。schema还分为三种:手动模式,自动模式和无schema模式。
域类型
域类型定义了应该如何从域中解析数据以及域是怎样被查询的。
- name: 域类型的名称,强制性必须指定的,且必需保证同一个schema文件内唯一性;
- class:域类型对应的Java类,强制性必须指定的,如果是solr内置的域类型,使用solr.类名方式指定即可,如果是自定义的域类型,必须指定类的完整路径;
- positionIncrementGap: 配置多值之间的间隙,用于组织伪短语匹配,此配置适用于多值域;
- autoGeneratePhraseQueries:若设置为true,solr会自动为相邻的term生成短语查询,若设置false,term必须被一对双引号包裹才能被认为是一个短语查询,此设置针对TextField;
- docValuesFormat:配置DocValuesFormat实现,这个配置需要提前在solrconfig.xml中配置SchemaCodecFactory;
- postingsFormat:postingFormat表示用于编/解码term,postings,数据的实现类,这个配置需要提前在solrconfig.xml中配置SchemaCodecFactory;
- 配置域类型为TextField,则还需要配置<analyzer>元素来指定分词器;
- 域类型属性依赖于具体实现类;
<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>
Solr内置预定义域类型
- BinaryField:表示经过base64编码的字符串域类型,你需要吧binary数据进行base64编码才能被solr进行索引;
- BoolField:表示boolean类型的数据,除了true,false,第一个字符是“1”,“t”,“T”也会被认为true;
- CollationField:支持Unicode排序规则进行排序和区间范围查询,当你使用ICU4J时,选择使用ICUCollationField是更好的选择;
- CurrencyField:支持货币以及货币兑换和货币汇率换算;
- DateRangeField:支持对日期范围进行索引,从而支持日期范围查询;
- ExternalFileField:对外部文件进行索引的域类型,他支持从外部硬盘上读取指定文件的内容创建索引;
- EnumField:对枚举进行索引的域类型;
- ICUCollationField:支持Unicode排序规则进行排序和区间范围查询;
- LatLonType:用于对经纬度数据进行索引,经纬度数据格式必须为latitude,longitude,纬度在前,经度在后,两者之间用逗号隔开,且两者之间不能有空格等其他字符,且不支持多值。适用于solr中spatial search(空间查询);
- PointType:支持对N个纬度的坐标定数据的索引,比如数学里的二维坐标x,y,甚至立体几何的三位坐标x,y,z,但不支持多值;
- PreAnalyzedField:即预分词域,他提供一个序列化的token stream传递给solr,这样solr 在对PreAnalyzed-Field创建索引时不需要进行分词了,因为如何分词处理已经提前在序列化的token stream中定义好了;
- RandomSortField:这个域类型不包含域值,他用于查询时,根据RandomSortField排序时,将会对返回结果进行随机排序,一般与dynamicField搭配使用;
- SpatialRecursivePrefixTreeFieldType:用于深度便利前缀树的FieldType,主要用于获取lucene中的RecursivePrefixTreeStrategy。这个域用于solr中的Spatial Search(空间查询);
- StrField:对UTF-8编码的字符串或者Unicode字符进行索引,StrField不会分词,且StrField有个硬性限制,字符大小必须小于32KB。他支持docValues域,但当为其添加了docValues域,则要求只能是单值域且该域必须存在或者该域有默认值;
- TextField:用于对长文本进行索引,域StrField最大区别是,TextField会分词且无长度限制;
- TrieDateField:Date域类型,表示一个毫秒精度对时间点,启用precisionStep=“0”会提升Date域对排序性能并最大限度的减少索引大小,配置precisionStep=“8”会提升Date域的范围区间查询性能;
- DateRangeField:用于对日期范围进行索引,并且兼容TrieDateField;
- TrieDoubleField:Double域类型,precisionStep=“0”会提升Double域的排序性能并最大限度减少索引大小,配置precisionStep=”8“会提升Double域的范围查询性能;
- TrieFloatField:Float域类型,同TrieDoubleField类似;
- TrieIntField:Integer域类型,同TrieDoubleField类似;
- TrieLongField:Long域类型,同TrieLongField类似;
- TrieField:如果你使用这个域,那么还需要指定一个type属性,他的可选值为integer,long,float,double,date。使用这个域跟使用TrieXXXField是一样的;
- UUIDField:UUID域,如果域值为空字符串,或者null或者字符串“NEW”,那么solr会自动生成UUID字符串并创建索引,如果用户设置了域值,那么solr会首先检查域是否符合UUID规范,如果不符合,会抛出异常,否则会将域值转换为小写形式,最后穿件索引。在solrcloud环境下,配置一个UUIDField并且指定域值为“NEW”是不建议的,此时建议是用UUIDUpdateProcessorFactory来代替;
几个重点域类型讲解
- CurrencyField:
提供对货币值的支持,solr可以在查询时使用他完成货币的兑换和货币汇率换算,该域支持以下功能:
- 区间范围查询
- Function区间范围查询
- 排序
- 货币代号以及货币符号(比如美元的$,人民币的¥)的解析
- 支持对称和非对称汇率
配置案例1:通过FileExchangeRateProvider加载currencyConfig.xml配置文件
schema.xml配置
<fieldType name="currency" class="solr.CurrencyField" precisionStep="8" defaultCurrency="USD" currencyConfig="currency.xml" />
currency.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="BRL" rate="1.881093" comment="BRAZIL Real" />
<rate from="USD" to="CAD" rate="1.030815" comment="CANADA Dollar" />
<rate from="USD" to="CLP" rate="519.0996" comment="CHILE Peso" />
<rate from="USD" to="CNY" rate="6.387310" comment="CHINA Yuan" />
<rate from="USD" to="CZK" rate="18.47134" comment="CZECH REP. Koruna" />
<rate from="USD" to="DKK" rate="5.515436" comment="DENMARK Krone" />
<rate from="USD" to="HKD" rate="7.801922" comment="HONG KONG Dollar" />
<rate from="USD" to="HUF" rate="215.6169" comment="HUNGARY Forint" />
<rate from="USD" to="ISK" rate="118.1280" comment="ICELAND Krona" />
...
</rates>
</currencyConfig>
配置案例2:通过OpenExchangeRatesOrgProvider调用远程接口获取汇率数据
<fieldType name="currency" class="solr.CurrencyField" precisionStep="8" providerClass="solr.OpenExchangeRatesOrgProvider" refreshInterval="60" ratesFieldLocation="http://www.openexchangerates.org/api/latest.json?appid=your-personalAppIdKey"/>
- providerClass:表示provider实现类,内置的provider只需以solr.开头来代替默认包名即可,如果自定义实现的provider类,则需要指定完整包路径;
- refreshInterval:表示每间隔多少分钟在重新从远程接口获取一次汇率数据;
- ratesFileLocation:获取货币汇率数据的远程接口URL;
- TrieDateField:
用于对日期时间类型数据进行索引,日期时间类型数据在Java里可以使用java.util.Date,Long,Integer来表示,Long、Integer类型可以表示毫秒数;使用时需要遵循以下格式约束:YYYY-MM-DDThh:mm:ssZ
- YYYY表示年份
- MM表示月份,两位数字表示
- DD表示日期,两位数字表示
- hh表示小时数,24小时制
- mm表示分钟数
- ss表示秒数
- Z表示UTC时区
solr对于日期时间存储只能精确到毫秒,任何超出毫秒经度部分的数据将会被丢弃,因此如果需要精确到毫秒,那么日期时间数据格式应该类似如下:
1972-05-20T17:33:18.772Z
1972-05-20T17:33:18.77Z
1972-05-20T17:33:18.7Z
可以通过以下代码将以上日期时间转换成Date对象:
String input = "1972-05-20T17:33:18.772Z";
TimeZone utc = TimeZone.getTimeZone("UTC");
SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
f.setTimeZone(utc);
GregorianCalendar cal = new GregorianCalendar(utc);
cal.setTime(f.parse(input));
Date date = cal.getTime();
一个TrieDateField域类型完整配置如下:
<field name="Date" class="solr.TrieDateField" datetimeformat="yyyy=MM-dd'T' HH:mm:ss.sss'Z'" indexed="true" multivalued="false" stored="true" precisionStep="6" type="date">
</field>
- name:表示域类型的名称
- class:表示域类型的实现类
- detetimeformat:表示TrieDateField域的与之最终现实的字符串格式
- indexed:表示是否对当前TrieDateField域创建索引
- multivalued:表示是否为多值域
- stored:表示是否存储域值
- type:表示TrieDateField域的原始域值的数据类型,可选值:INTEGER,LONG,FLOAT,DOUBLE,DATE
- precisisonStep:该属性值越大,生成的term个数越少,索引体积也会小,但区间范围查询性能也越低;反之,生成的term个数越多,索引体积越大,范围区间查询性能越高。如类型为Integer,则占4个字节32位,步长precisionStep=4时,那么生成这些term,前4bit,前8bit,前12bit,前16bit...直到32bit。
- DateRangeField:
该域类型与TrieDateField类似,唯一的区别在于,该类型会以String类型暴露域值而非Date类型。
使用该域类型时,日期范围字符串必须类似如下:[2000 TO 2014-05-21T17:33:18.772Z],并支持日期范围的多值域索引。
案例:查询出某个时间段内正开放经营的餐厅。需要注意的是,每个餐厅在一天之内可以有多个开放时间,而且每天的开放时间不固定。
<field name="openingHours" type="date_range" indexed="true" stored="true" multiValued="true"/>
<fieldType name="date_range" class="solr.DateRangeField"/>
date_range域的数据:
[
"[2016-02-01T03:00Z TO 2016-02-01T15:00Z]",
"[2016-02-02T03:00Z TO 2016-02-02T15:00Z]",
"[2016-02-03T03:00Z TO 2016-02-03T15:00Z]",
"[2016-02-04T03:00Z TO 2016-02-04T15:00Z]",
"[2016-02-05T03:00Z TO 2016-02-05T16:00Z]",
"[2016-02-06T03:00Z TO 2016-02-06T16:00Z]"
]
可以通过如下查询:facet.range=openingHours&f.openingHours.facet.range.start=NOW&f.openingHours.facet.range.end=NOW%2B6HOUR&f.openingHours.facet.range.gap=%2B1HOUR
- facet.range:表示你需要对哪个域进行区间查询
- facet.range.start:表示查询区间的起始值
- facet.range.end:表示查询区间的结束值
- facet.range.gap:表示区间递增的间隙
4.EnumField:
该域类型用于对枚举类型的数据进行索引。
public enum LogLevel {
INFO,
DEBUG,
WARING,
ERROR
}
EnumField配置
<fieldType name="logLevelType" class="solr.EnumField" enumsConfig="enums-Config.xml" enumName="logLevel"
- enumsConfig:用于指定枚举配置文件,在其中定义支持的所有枚举值
- enumName:表示枚举名称,他必须域枚举配置文件中配置的某一个<enum>元素的name属性保持一致
<?xml version="1.0"?>
<enumsConfig>
<enum name="logLevel">
<value>INFO</value>
<value>DEBUG</value>
<value>WARNING</value>
<value>ERROR</value>
</enum>
<enum name="priority">
<value>HIGH</value>
...
</enum>
...
</enumsConfig>
- ExternalFileField:
该域类型支持为某个域指定域值,而该域值是从外部文件加载得到的。也就是说,某个域的与之不需要存储到Solr中,可以从外部文件获取。ExternalFileField是不支持查询的,它只能用于function query或者显示。
案例:假设你的Document有一个views域,而这个views域可能表示的是一篇博客的浏览量,由于浏览量可能每分每秒都在改变,你可能希望每个1分钟或者半小时单独更新下views表示浏览量的域,而文档其他域的值可能更新并不是那么频繁。
<fieldType name="viewsFile" keyField="id" defVal="0" stored="false" indexed="false" class="solr.ExternalFileField" valType="pfloat"/>
- keyField:定义唯一性标示域的名称,它通常示Document的唯一主键域,但并不是必须的,只要keyField能唯一标示一个Document即可
- defValue:表示当某个Document在外部文件中不存在示,ExternalFileField的域值采用defVal定义的默认值
- valType:定义需要在外部文件里加载的域值的时机数据类型,可选值为pfloat,float,tfloat,这个属性示可选的,可以不用配置
- ExternalFileField依赖的外部文件默认需要存放在$SOLR_HOME/data目录下,文件名称应该满足如下规则:external_filename或者external_fileanme.*
eg.external_entryRankFile或者external_entryRankFile.txt。如果匹配到多份文件,以最后一份为准。
文件内容如下:
1=1000
2=20000
3=999
左边表示keyField参数表示的域的域值,右边表示需要存储到外部文件而且经常需要更新的域的域值。至此,我们只需要在field定义里通过type=“viewsFile”来应用ExternalFileField,之后更新veiews域的域值就不需要更新每一份Document而只需周期性更新外部文件的键值和对应的数值即可。
- UUIDField:
使用场景:
- 当你需要增量式的添加索引文档,索引文档可能会重复添加,而你不希望索引文档出现初伏
- 当你的索引的数据很大,可能不会把它存储到solr索引中,但有希望后续能够根据索引中的某个域从外部数据源再次加载该数据
- 当你希望更简便的使用唯一主键来删除一个文档,而不是每次都繁琐的使用query来匹配待删除文档
- 当你是用DistributedSearch(分布式查询)时,需要一个唯一键来过滤多个分片上返回的重复文档,因为你不希望将重复的文档返回给用户。
UUID配置:
<!--schema.xml-->
<fieldType name="uuid" class="solr.UUIDField" indexed="true" />
<field name="id" type="uuid" indexed="true" stored="true" multiValued="false" required="true"/>
经过上面步骤,我们的UUIDField域已经配置好了,但solr还提供了一个自动更新UUIDField的处理器,也就是使用它UUIDUpdateProcessorFactory,我们就不需要关心如何创建和更新UUIDField域了,全权交给UUIDUpdateProcessorFactory处理就行了。
<!--solrconfig.xml-->
<updateRequestProcessorChain name="dispup">
<processor class="solr.UUIDUpdateProcessorFactory">
<str name="fieldName">id</str>
</processor>
<processor class="solr.LogUpdateProcessorFactory"/>
<processor class="solr.DistributedUpdateProcessorFactory"/>
<processor class="solr.RunUpdateProcessorFactory"/>
</updateRequestProcessorChain>
<requestHandler name="/update" class="solr.UpdateRequestHandler">
<lst name="defaults">
<str name="update.chain">dispup</str>
</lst>
</requestHandler>