Solr 对文本索引及其查询

2019/02/25 Solr

关于 Solr 如何对文本数据做索引(indexing)。

简介

Solr 在建立索引和处理查询的过程中,都对文本数据进行了预处理。这个过程涉及了 Solr 的三个组件:Analyzer, Tokenizer 以及 Filter。

Analyzer

Analyzer 负责检查文本字段以及把文本转换成 token 流,在建立索引和处理查询的过程中都会被调用。一个 Analyzer 可以是一个单独的类,例如:

<fieldType name="nametext" class="solr.TextField">   
  <analyzer class="org.apache.lucene.analysis.core.WhitespaceAnalyzer"/>
</fieldType>

也可以是几个的 Tokenizer 类和 Filter 类的组合:

<fieldType name="nametext" class="solr.TextField">
  <analyzer>
    <tokenizer class="solr.StandardTokenizerFactory"/>     
    <filter class="solr.LowerCaseFilterFactory"/>     
    <filter class="solr.StopFilterFactory"/>    
    <filter class="solr.EnglishPorterFilterFactory"/>  
  </analyzer>
</fieldType>

Tokenizer

Tokenizer 负责把文本切分成 token,也就是通常所说的“分词”。分词的粒度可以是字级别,也可以是词级别,这个通过在配置文件中配置不同的 Tokenizer 类来控制。Solr 内置了多种 Tokenizer 类,在官方文档有详细的介绍。

Filter

Filter 负责检查 token 流,根据需要对 token 流进行插入、替换、删除操作,从而产生一个新的 token 流。例如删除停用词、大小写转换、单词原型化等,都是由 Filter 来处理。

更多细节参见官方文档

索引字段查询

《Solr 快速入门手册》第 2.3 节 中提到我们可以在导入数据的时候指定索引的字段(field)。

在定义字段的时候,指定后缀 _t,即可对指定字段根据** token **建立索引。Solr 内置了语种检测的功能。在默认情况下,Solr 对英文文本按空格切分为 token,对中文文本按字切分为 token。

举个例子。

运行下面的命令插入一条数据:

$ curl http://localhost:8983/solr/demo/update -d '
[
 {"id" : "book1",
  "title_t" : "The Way of Kings",
  "author_s" : "Brandon Sanderson"
 }
]'

其中 title_t 是建了索引的列。

运行查询语句:

$ curl http://localhost:8983/solr/demo/query?q=title_t:Kings

返回结果为:

{
  "responseHeader":{
    "status":0,
    "QTime":3,
    "params":{
      "q":"title_t:Kings"}},
  "response":{"numFound":1,"start":0,"docs":[
      {
        "id":"book1",
        "title_t":"The Way of Kings",
        "author_s":"Brandon Sanderson",
        "_version_":1601959657026355200}]
  }}

说明,用 Kings 作为关键词,检索成功。

运行查询语句:

$ curl http://localhost:8983/solr/demo/query?q=title_t:King

返回结果为:

{
  "responseHeader":{
    "status":0,
    "QTime":1,
    "params":{
      "q":"title_t:King"}},
  "response":{"numFound":0,"start":0,"docs":[]
  }}

因为 King 在 token 的维度上无法匹配 Kings,所以用 King 作为关键词检索失败。

这一点在 Solr Tutorial 中提到:

Indexed for full-text search so individual words or phrases may be matched.

同时,Solr 默认支持 token 级别的模糊查询,例如这个查询(在命令行运行时,需要把空格转换成 %20):

$ curl "http://localhost:8983/solr/demo2/query?q=title_t:"The%20Kings""

返回结果为:

{
  "responseHeader":{
    "status":0,
    "QTime":0,
    "params":{
      "q":"title_t:The Kings"}},
  "response":{"numFound":1,"start":0,"docs":[
      {
        "id":"book1",
        "title_t":"The Way of Kings",
        "author_s":"Brandon Sanderson",
        "_version_":1626406079247155200}]
  }}

The Kings 作为关键词来查询,发现尽管 The Way of Kings 不包含 The Kings 字符串,但由于包含 TheKings 的 token,仍然发生了匹配。

而把 The 改成 Th 之后,则下面这个查询语句无法匹配:

$ curl "http://localhost:8983/solr/demo2/query?q=title_t:"Th%20Kings""

返回结果为:

{
  "responseHeader":{
    "status":0,
    "QTime":1,
    "params":{
      "q":"title_t:Th Kings"}},
  "response":{"numFound":0,"start":0,"docs":[]
  }}

另外,如果 _t 后缀的 field 的内容是 json 格式的字符串,会把 json object 中的 key 和 value 的字符串作为 token 来建立索引。

查询语句的引号

某次踩坑的时候发现,Solr 查询语句中的引号有不同处理。在上面那个例子中,虽然我们在查询语句中写的是 q=title_t:"The%20Kings",但实际发生的查询中 The Kings 是没有带引号的,在查询结果中的 "params":{"q":"title_t:The Kings"} 可以看出。

如果加上引号会怎么样呢?测试如下:

运行:

$  curl "http://localhost:8983/solr/demo2/query?q=title_t:\"The%20Kings\"" 

结果:

{
  "responseHeader":{
    "status":0,
    "QTime":0,
    "params":{
      "q":"title_t:\"The Kings\""}},
  "response":{"numFound":0,"start":0,"docs":[]
  }}

从结果中的 可以看到我们给 The Kings 加上了引号,然后没有匹配到结果。这是因为加上引号之后 The Kings 会作为一个整体来匹配。相关内容在官方文档的 Specifying Terms for the Standard Query Parser 中提到:

A query to the standard query parser is broken up into terms and operators. There are two types of terms: single terms and phrases.

A single term is a single word such as "test" or "hello"

A phrase is a group of words surrounded by double quotes such as "hello dolly"

Multiple terms can be combined together with Boolean operators to form more complex queries (as described below).

其他参考

Solr 的分析器,分词器和分词过滤器

Solr理解Analyzers, Tokenizers, and Filters

Search

    Table of Contents