shoufeng

瘦风的南墙

Solr 03 - 解读Solr的schema.xml文件 (Solr的模式设计与优化)

1 关于schema.xml文件

Solr将数据以结构化的形式存储到文件系统中, 在存储的过程中对数据建立索引 —— 通过模式文件schema.xml文件来定义这个结构.

schema.xml文件位于Solr安装包的 example/solr/collection1/conf/目录下, 主要用于配置Solr的域(Field)以及域的类型(FieldType).

说明事项:

Solr中Field相关内容要先配置再使用;

schema.xml文件的名称不可以更改;

在使用中也应该位于SolrHome/conf/ 下 或者位于 Solr Web 应用的类加载器可以加载到的位置.

这里演示的schema.xml文件是Solr 4.10.4版本中的模式文件.

2 解读schema.xml文件

文件示例:

<?xml version="1.0" encoding="UTF-8" ?>
<schema name="user" version="1.5">
   <field name="_version_" type="long" indexed="true" stored="true"/>
   <field name="_root_" type="string" indexed="true" stored="false"/>
   <field name="id" type="string" indexed="true" stored="true" required="true" multiValued="false" />
   <field name="name" type="text_general" indexed="true" stored="true"/>
   <uniqueKey>id</uniqueKey>

   <fieldType name="long" class="solr.TrieLongField" precisionStep="0" positionIncrementGap="0"/>
   <fieldType name="string" class="solr.StrField" sortMissingLast="true" />
   <fieldType name="text_general" class="solr.TextField" positionIncrementGap="100">
      <analyzer type="index">
        <tokenizer class="solr.StandardTokenizerFactory"/>
      </analyzer>
      <analyzer type="query">
        <tokenizer class="solr.StandardTokenizerFactory"/>
      </analyzer>
    </fieldType>
</schema>

(1) 根元素是schema, name属性值用来展示, 可以任意配置, version是Solr模式语法和语义的版本号, Solr 4.10版本中使用1.5版本.

1.0版本: multiValued属性不存在, 所有字段本质上都是多值的;
1.1版本: 引入了multiValued属性, 默认为false;
1.2版本: 引入了omitTermFreqAndPositions属性, 默认为true, 文本字段除外;
1.3版本: 删除了可选的字段压缩功能;
1.4版本: autoGeneratePhraseQueries属性, 用于在单个字符串生成多个标记时驱动QueryParser行为. 1.4及以上的版本默认关闭;
1.5版本: 对于原始字段类型(如int, float, boolean, string...), omitNorms默认为true.

(2)根元素下主要有field和fieldType两个标签, 分别用来定义域和域的类型.

2.1 field - 配置域

<field name="id" type="string" indexed="true" stored="true" required="true" multiValued="false" />

field 类似于MySQL中数据表的字段, 属性包括name, type(就是之前定义过的fieldType)等等, 具体属性说明如下:

(1)必需属性:

name: 域的名称, 以字母或下划线开头, 不能由数字开头;
type: 下面将定义的fieldType域的类型;
indexed: 如果此字段需要被检索和排序, 就设置为true;
stored: 如果这个字段在查询的时候需要显示出来, 就要设置为true, 表示存储它的原始值, 如果在检索的时候不需要显示原始值, 就尽量设置为false, 尤其是值比较大的字段;
required: 表明该字段是必填字段, 类似于MySQL中的not null. 若设置为true, 在创建索引时, 就必须包含该字段的值, 如过不存在, 就会抛出错误;
multiValued: 是否有多个值(在Solr中允许一个域保存多个值, 类似于MySQL中一个用户可以存储多个好友, 一件商品有多个细节图片).

原配置文件中的英文说明:

Although it would make indexing slightly slower and the index bigger, it would also make the index faster to load, more memory-efficient and more NRT-friendly.
大概意思是: 虽然它会使索引略微变慢、索引文件变得更大, 但它却能使得索引更快被加载, 内存使用效率更高, 并且更友好的NRT(近实时搜索).

(2)可选属性:

docValues: 是否为当前field添加一个名为docValues的field --- 这对facet查询、group分组、排序、function查询有好处.

a) 不足: 会使得索引过程略慢, 且索引文件更大;
b) 优点: 能加快索引数据的加载, 对NRT(近实时搜索)也更加友好, 而且更节省内存;
c) 限制: 目前docValues只支持strField、UUIDField、Trie*Field; 要求字段的值是单值, 并且此field的值必须存在或具有默认值.

compressed: 是否使用gzip压缩(只有TextField和StrField可以压缩), 默认为false;

omitNorms(专业属性): 是否忽略掉Norm, 如果设置为true, 将忽略与此字段相关联的规范 (这将禁用字段的长度规范化和检索时文档权重分的提升, 并节省一些内存空间). 只有全文本的field和需要检索时提升权重的field需要norm; 只有全文本类型的字段, 或者需要在检索时对当前字段的权重进行设置时, 才需要相关规范, 此时需要将其设置为false. 默认情况下, 原始(未分析)类型的规范将被忽略.

termVectors: 默认false, 为true时会存储当前字段的term vector(术语向量), 使用MoreLikeThis时, 用来作为相似性匹配的field的stored应该设置为true, 以提高性能;

termPositions: 使用term vector存储位置信息, 这会增大存储成本;

termOffsets: 使用term vector存储偏移量信息, 这会增大存储成本;

default: 设置当前字段的默认值, 如果在添加文档时没有为该字段指定值, 将使用此默认值.

(3)注意事项:

name="_version_" 的字段必须添加, 用来记录Solr中文档的版本. 如果删除此字段, 则必须禁用solrconfig.xml文件中的更新日志, 否则Solr将无法启动. SolrCloud需要_version_和更新日志.

name="_root_" 的字段用来指向嵌套文档块的根文档, 有嵌套文档时就必需存在此字段, 否则嵌套文档可能会被删除.

③ 几乎所有的Solr文档中都存在<uniqueKey>id</uniqueKey> —— 主键为id, 如果没有充分的理由, 请不要删除id字段. ⇒ 当然可以根据实际情况配置符合自身业务的主键.

2.2 fieldType - 配置域类型

fieldType供Solr内部使用, 不支持自定义域类型. 原始FieldType定义示例:

<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>

(1)必需属性:

name: 域类型的名称, 是field中的type;
class: 域类型对应的Solr的标准Java类, 以"Solr"开头的class是org.apache.solr.analysis包中的Java类;
③ analyzer: 定义索引和搜索使用的分词器, **是核心配置 --- 使用的分析器不同, 索引和检索也将不同**; ④ type: index(索引流程)和query(检索流程); ⑤ tokenizer: 指定具体使用的分词器; ⑥ filter`: 指定使用的过滤器;

(2)可选属性:

positionIncrementGap: 当multiValued="true" 时使用, 定义在同一个文档中此类型数据的空白间隔, 即设置多个值之间的虚拟空白的数量, 避免短语匹配错误.

autoGeneratePhraseQueries: 有点类似找近义词或者自动纠错, 例如可以将 "wi fi" 自动转为 "wifi" 或 "wi-fi", 如果不设置这个属性则需要在查询时强制加上引号, 如 'wi fi';

sortMissingLast / sortMissingFirst: 对查询结果进行排序的过程中, 如果发现这个字段的值不存在, 就排在前面/后面, 忽略排序的条件. 使用方式如下:

a) 默认值均为false, 将使用Lucene内部的排序规则: 将没有该字段的文档放在升序中, 最后放在降序结果中. ;
b) sortMissingLast="true", 对该字段进行排序时, 没有该字段的文档将排在有该字段的文档的后面, 忽略请求时的排序规则(asc或desc);
c) sortMissingFirst="true", 与sortMissingLast作用相反.

(3)常见域类型(class):

solr.StrField:

<fieldType name="string" class="solr.StrField" sortMissingLast="true" />

此类型不会被分词, 而是被逐字索引/存储. 它支持docValues域, 如果使用此fieldType的field添加了docValues字段, 则要求该field只能是单值域且该域必须存在或者该域有默认值.

solr.BoolField:

<fieldType name="boolean" class="solr.BoolField" sortMissingLast="true"/>

boolean域, 对应true/false.

solr.Trie*Field: 默认的数字域类型:

<fieldType name="int" class="solr.TrieIntField" precisionStep="0" positionIncrementGap="0"/>
<fieldType name="float" class="solr.TrieFloatField" precisionStep="0" positionIncrementGap="0"/>
<fieldType name="long" class="solr.TrieLongField" precisionStep="0" positionIncrementGap="0"/>
<fieldType name="double" class="solr.TrieDoubleField" precisionStep="0" positionIncrementGap="0"/>

这些字段支持docValues, 但它们要求field为单值, 并且要么是必需的, 要么具有默认值.

solr.Trie*Field: 更快的范围查询的数字域类型:

如果需要更快的范围查询, 请考虑 tint / tfloat / tlong / tdouble 类型:

<fieldType name="tint" class="solr.TrieIntField" precisionStep="8" positionIncrementGap="0"/>
<fieldType name="tfloat" class="solr.TrieFloatField" precisionStep="8" positionIncrementGap="0"/>
<fieldType name="tlong" class="solr.TrieLongField" precisionStep="8" positionIncrementGap="0"/>
<fieldType name="tdouble" class="solr.TrieDoubleField" precisionStep="8" positionIncrementGap="0"/>

此类型 以多种精度级别对每个值创建不同的索引 , 用来加速范围两端间距较大时的范围查询.

precisionStep: 一般用于数字范围查询, 其值越小(以位为单位), 索引时该域的值分出的token个数越多 ⇒ 索引体积会稍大(slightly larger index size) ⇒ 但这能加快数字范围检索的响应速度.

precisionStep="0"将禁用不同精度级别的索引.

positionIncrementGap: 如果当前域是多值域时, 多个值之间的间距. 该属性在单值域上无意义.

solr.TrieDateField: 日期域类型:

<fieldType name="date" class="solr.TrieDateField" precisionStep="0" positionIncrementGap="0"/>

日期字段只支持格式为1995-12-31T23:59:59Z的日期, 最后的 Z 表示UTC时间, 并且必须是UTC时间.
其中, 只有秒数可以选用小数: 1995-12-31T23:59:59.999Z, 其他部分都是必需且格式不能改变的.

日期表达式也可用于表示相对于"NOW"(当前时刻)的时间, 如:

NOW/HOUR ⇒ 回到当前小时的开始时间;
NOW-1DAY ⇒ 恰好在前一天;
NOW/DAY+6MONTHS+3DAYS ⇒ 从当天开始, 将来6个月零3天的时刻.

注意: 对于更快的范围的查询和日期分面的需求, 请考虑使用tdate类型.

<fieldType name="tdate" class="solr.TrieDateField" precisionStep="6" positionIncrementGap="0"/>

solr.BinaryField:

<fieldtype name="binary" class="solr.BinaryField"/>

经过Base64编码的字符串域类型, 就是说需要把数据进行Base64编码, 然后再发送/检索.

solr.RandomSortField:

<fieldType name="random" class="solr.RandomSortField" indexed="true" />

随机排序域类型, 不用于存储或搜索任何数据, 可以在模式中声明此类型的字段, 以实现文档的伪随机排序.

⇒ 根据字段名称和索引的版本生成排序. 只要索引的版本_version_不变, 并且重用相同的字段名称, 文档的顺序就会一致.
⇒ 如果需要不同的伪随机文档排序, 对于相同版本的索引, 请使用dynamicField并更改请求中的字段名称.

solr.TextField:

使用最多的一种域类型, 需要分词, 一般需要用户配置分析器来定制索引和查询, 分析器包括一个分词器(tokenizer)和多个过滤器(filter). 示例:

<!-- 空格分词器, 精确匹配: 
     在向索引库添加text类型的索引时, Solr会首先用空格进行分词, 
     然后把分词结果依次使用指定的过滤器进行过滤, 
     最后剩下的结果才会加入到索引库中以备查询. 
     注意: Solr的analysis包并没有支持中文的包, 需要自己添加中文分词器.
-->
<fieldType name="text_ws" class="solr.TextField" positionIncrementGap="100">
   <analyzer>
      <tokenizer class="solr.WhitespaceTokenizerFactory" />
   </analyzer>
</fieldType>

(4)常见的过滤器(filter):

① 分隔符过滤器:

<!-- 在分词和匹配时, 考虑 "-"连字符, 字母数字的界限, 非字母数字字符, 
     这样"wifi"或"wi fi"都能匹配"Wi-Fi". 
-->
<filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" 
        generateNumberParts="1" catenateWords="1" catenateNumbers="1" 
        catenateAll="0" splitOnCaseChange="1" />

② 同义词过滤器:

<filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" 
        ignoreCase="true" expand="true" />

③ 停词(禁用词)过滤器:

<!-- 在禁用字(stopword)删除后, 在短语间增加间隔stopword: 
      即在建立索引过程中(建立索引和搜索)被忽略的词, 比如is this等常用词. 
      在conf/stopwords.txt文件中设置维护. 
-->
<filter class="solr.StopFilterFactory" ignoreCase="true" 
        words="stopwords.txt" enablePositionIncrements="true" />

2.3 copyField - 配置复制域

Solr允许将多个域的值, 复制给一个域, 目的是方便搜索.

比如: 搜索含有"Solr"的文档, 需要把标题、摘要信息、正文中含有"Solr"的文档都查询出来, 有了copyField域, 就只需要搜索copyField域, 而不需要写全标题、摘要信息、正文三个域的值.

在创建索引的时候, Solr会将源域的内容, 复制给目标域.

<copyField source="cat" dest="text"/>

(1)属性说明:

source: 源域的名称;
dest: 目标域的名称, 搜索时指定目标域为默认搜索域, 可提高查询效率.

(2)使用注意事项:

目标域的定义, 必须要指定multiValued="true".
② 只复制单个域, 如果被复制域本身就是多值域, 那么目标域也是多值域 ⇒ 无实际意义;
③ 若复制多个域, 只要其中有一个域是多值域, 那么目标域就一定是多值域.

2.4 dynamicField - 配置动态域

动态域通过通配符, 实现了域的模糊匹配, 能够有效避免频繁地修改schema.xml文件.
⇒ 修改schema.xml文件后, Solr程序需要重新加载配置文件, 较为繁琐.

dynamicField(动态域) 和 copyField (复制域)是Solr扩展的域, 在Lucene中没有与之相关的概念.

<dynamicField name="*_i" type="int" indexed="true" stored="true"/>

(1)属性说明:

name: 动态域的名称, 是一个表达式:

a) *表示匹配任意字符, 只能出现在模式的最前或最后;
b) 较长的模式会先做匹配;
c) 如果2个模式同时匹配上, 最先定义的优先匹配.

(2)使用说明:

使用时, 只需要域的名称与表达式匹配即可. 如 "product_i":"笔记本" , 直接使用product_i就可以, 不需要再做其他定义.
如果通过上面的匹配都没找到, 可以设置下述类型的配置, 然后定义一个type当作String处理. 一般不会发生, 但若不定义, 找不到匹配就会报错.

<dynamicField name="*_i" type="ignored" multiValued="true"/>

2.5 uniqueKey - 配置唯一标识

Solr通过uniqueKey来指定文档的主键, 默认的, id域被作为主键, 因此id域是必须的.

⇒ 当然用户可以自定义主键.

<uniqueKey>id</uniqueKey>

Solr中被标记为required="true"的字段, 必须要由<uniqueKey>作唯一标识, 否则建立索引时将报错.

Solr将根据<uniqueKey>标识的field (默认就是id) 来决定增量导入时是否重复导入: 如果id相同, 就不会重复导入;

2.6 其他配置说明

(1) 默认搜索域:

<defaultSearchField>text</defaultSearchField>

如果搜索参数中没有指定具体的field, 这将作为默认的域进行搜索.

Solr 6.x版本中已经移除了此参数.

(2) Solr查询解析器:

<!-- 从Solr 6.6版本开始, 不再支持defaultOperator -->
<solrQueryParser defaultOperator="OR"/>

配置搜索参数短语间的逻辑, 可以是AND|OR.

3 关于schema.xml的优化

3.1 field的配置技巧

(1) 将所有只用于搜索、而不需要作为结果返回原始值的field(特别是比较大的field)设置为 stored="false";

(2) 将不需要被用于搜索、而只是作为结果返回的field设置为 indexed="false";

(3) 删除所有不必要的copyField;

(4) 为了最小化索引字段、提高搜索效率, 将为所有的text field 都设置 index="false", 然后使用copyField将它们都复制到一个总的text field上, 对该text field进行搜索;

(5) 为了最大化搜索性能, 使用ConcurrentUpdateSolrServer Java客户端与Solr交互(使用并发修改服务);

(6) 在服务器端运行JVM, 即设置使用-server服务端模式, 性能更好; 并使用尽可能高级别的Log输出等级, 避免记录每个请求, 减少日志量.

3.2 field的配置示例

分词 搜索 存储 示例
网页的标题、内容
X 网页的发布时间
X X 引用的图片位置
网页标题、内容

不存在不需要索引、不需要分词、也不需要存储的字段, 因为这样的字段在Lucene中无意义.

参考资料

Solr:Schema设计
Solr Wiki - SchemaXml

版权声明

作者: 马瘦风(https://healchow.com)

出处: 博客园 马瘦风的博客(https://www.cnblogs.com/shoufeng)

感谢阅读, 如果文章有帮助或启发到你, 点个[好文要顶👆] 或 [推荐👍] 吧😜

本文版权归博主所有, 欢迎转载, 但 [必须在文章页面明显位置标明原文链接], 否则博主保留追究相关人员法律责任的权利.

posted @ 2018-08-12 18:38  瘦风  阅读(1600)  评论(0编辑  收藏  举报