分析器

配置分析器

第三个重要的索引设置是 analysis 部分,用来配置已存在的分析器或创建自定义分析器来定制化你的索引。standard 分析器是用于全文字段的默认分析器,对于大部分西方语系来说是一个不错的选择。它考虑了以下几点:

  • standard 分词器,在词层级上分割输入的文本。
  • standard 标记过滤器,被设计用来整理分词器触发的所有标记(但是目前什么都没做)。
  • lowercase 标记过滤器,将所有标记转换为小写。
  • stop 标记过滤器,删除所有可能会造成搜索歧义的停用词,如 atheandis

默认情况下,停用词过滤器是被禁用的。如需启用它,你可以通过创建一个基于 standard 分析器的自定义分析器,并且设置 stopwords 参数。可以提供一个停用词列表,或者使用一个特定语言的预定停用词列表。

  • 在下面的例子中,我们创建了一个新的分析器,叫做 es_std,并使用预定义的西班牙语停用词:
    PUT /spanish_docs
    {
        "settings": {
            "analysis": {
                "analyzer": {
                    "es_std": {
                        "type":      "standard",
                        "stopwords": "_spanish_"
                    }
                }
            }
        }
    }
    
  • es_std 分析器不是全局的,它仅仅存在于我们定义的 spanish_docs 索引中。为了用 analyze API 来测试它,我们需要使用特定的索引名。
    GET /spanish_docs/_analyze?analyzer=es_std
    El veloz zorro marrón
    
  • 下面简化的结果中显示停用词 El 被正确的删除了:
    {
      "tokens" : [
        { "token" :    "veloz",   "position" : 2 },
        { "token" :    "zorro",   "position" : 3 },
        { "token" :    "marrón",  "position" : 4 }
      ]
    }
    

自定义分析器

虽然 Elasticsearch 内置了一系列的分析器,但是真正的强大之处在于定制你自己的分析器。你可以通过在配置文件中组合字符过滤器,分词器和标记过滤器,来满足特定数据的需求。

  • 字符过滤器
    • 字符过滤器是让字符串在被分词前变得更加“整洁”。例如,如果我们的文本是 HTML 格式,它可能会包含一些我们不想被索引的 HTML 标签,诸如 <p><div>
    • 我们可以使用 html_strip 字符过滤器 来删除所有的 HTML 标签,并且将 HTML 实体转换成对应的 Unicode 字符,比如将 &Aacute; 转成 Á
    • 一个分析器可能包含零到多个字符过滤器。
  • 分词器
    • 一个分析器 必须 包含一个分词器。分词器将字符串分割成单独的词(terms)或标记(tokens)。standard 分析器使用 standard 分词器将字符串分割成单独的字词,删除大部分标点符号,但是现存的其他分词器会有不同的行为特征。
    • 例如,keyword 分词器输出和它接收到的相同的字符串,不做任何分词处理。[whitespace 分词器]只通过空格来分割文本。[pattern 分词器]可以通过正则表达式来分割文本。
  • 标记过滤器
    • 分词结果的 标记流 会根据各自的情况,传递给特定的标记过滤器。
    • 标记过滤器可能修改,添加或删除标记。我们已经提过 lowercasestop 标记过滤器,但是 Elasticsearch 中有更多的选择。stemmer 标记过滤器将单词转化为他们的根形态(root form)。ascii_folding 标记过滤器会删除变音符号,比如从 très 转为 tresngramedge_ngram 可以让标记更适合特殊匹配情况或自动完成。

在【深入搜索】中,我们将举例介绍如何使用这些分词器和过滤器。但是首先,我们需要阐述一下如何创建一个自定义分析器

创建自定义分析器

与索引设置一样,我们预先配置好 es_std 分析器,我们可以再 analysis 字段下配置字符过滤器,分词器和标记过滤器:

PUT /my_index
{
    "settings": {
        "analysis": {
            "char_filter": { ... custom character filters ... },
            "tokenizer":   { ...    custom tokenizers     ... },
            "filter":      { ...   custom token filters   ... },
            "analyzer":    { ...    custom analyzers      ... }
        }
    }
}

作为例子,我们来配置一个这样的分析器:

  1. html_strip 字符过滤器去除所有的 HTML 标签
  2. & 替换成 and,使用一个自定义的 mapping 字符过滤器
"char_filter": {
    "&_to_and": {
        "type":       "mapping",
        "mappings": [ "&=> and "]
    }
}
  1. 使用 standard 分词器分割单词
  2. 使用 lowercase 标记过滤器将词转为小写
  3. stop 标记过滤器去除一些自定义停用词。
"filter": {
    "my_stopwords": {
        "type":        "stop",
        "stopwords": [ "the", "a" ]
    }
}

根据以上描述来将预定义好的分词器和过滤器组合成我们的分析器:

"analyzer": {
    "my_analyzer": {
        "type":           "custom",
        "char_filter":  [ "html_strip", "&_to_and" ],
        "tokenizer":      "standard",
        "filter":       [ "lowercase", "my_stopwords" ]
    }
}

用下面的方式可以将以上请求合并成一条:

PUT /my_index
{
    "settings": {
        "analysis": {
            "char_filter": {
                "&_to_and": {
                    "type":       "mapping",
                    "mappings": [ "&=> and "]
            }},
            "filter": {
                "my_stopwords": {
                    "type":       "stop",
                    "stopwords": [ "the", "a" ]
            }},
            "analyzer": {
                "my_analyzer": {
                    "type":         "custom",
                    "char_filter":  [ "html_strip", "&_to_and" ],
                    "tokenizer":    "standard",
                    "filter":       [ "lowercase", "my_stopwords" ]
            }}
}}}

创建索引后,用 analyze API 来测试新的分析器:

GET /my_index/_analyze?analyzer=my_analyzer
The quick & brown fox

下面的结果证明我们的分析器能正常工作了:

{
  "tokens" : [
      { "token" :   "quick",    "position" : 2 },
      { "token" :   "and",      "position" : 3 },
      { "token" :   "brown",    "position" : 4 },
      { "token" :   "fox",      "position" : 5 }
    ]
}

除非我们告诉 Elasticsearch 在哪里使用,否则分析器不会起作用。我们可以通过下面的映射将它应用在一个 string 类型的字段上:

PUT /my_index/_mapping/my_type
{
    "properties": {
        "title": {
            "type":      "string",
            "analyzer":  "my_analyzer"
        }
    }
}