Press "Enter" to skip to content

ElasticSearch 文本分析

词条(term)查询和全文(fulltext)查询最大的不同之处是:全文查询首先 分析(Analyze)查询字符串,使用默认的分析器分解成一系列的分词,term1, term2,termN,然后从索引中搜索是否有文档包含这些分词中的一个或多个。

 

所以,在基于全文的检索里,ElasticSearch 引擎会先分析(analyze)查询字 符串,将其拆分成小写的分词,只要已分析的字段中包含词条的任意一个,或全 部包含,就匹配查询条件,返回该文档;如果不包含任意一个分词,表示没有任何文档匹配查询条件。

 

这里就牵涉到了 ES 里很重要的概念,文本分析,当然对应非 text 类型字段来说,本身不存在文本数据词项提取的问题,所以没有文本分析的问题。

 

1、文本分析

 

分析( analysis )是在文档被发送并加入倒排索引之前,Elasticsearch 在其主体 上进行的操作。在文档被加入索引之前,Elasticsearch 让每个被分析字段经过一 系列的处理步骤。

字符过滤: 使用字符过滤器转变字符。
文本切分为分词: 将文本切分为单个或多个分词。
分词过滤: 使用分词过滤器转变每个分词。
分词索引: 将这些分词存储到索引中。

例如下面一段话:”I want to play games,it include WOW&Diablo&Starcraft”,经过分析后的分词为: i want to play games it include WOW Diablo Starcraft

 

1.1、字符过滤

 

Elasticsearch 首先运行字符过滤器(char_filter),这些过滤器将特定的字符 序列转变为其他的字符序列。这个可以用于将 HTML 从文本中剥离,或者是将任 意数量的字符转化为其他字符,例如可将上面的 “I want to…..” 中的 & 转换为 AND

 

1.2、切分为分词

 

在应用了字符过滤器之后,文本需要被分割为可以操作的片段。底层的 Lucene 是不会对大块的字符串数据进行操作。相反,它处理的是被称为分词 (token) 的数据。

 

分词是从文本片段生成的,可能会产生任意数量(甚至是 0)的分词。例如, 在英文中一个通用的分词是标准分词器,它根据空格、换行和破折号等其他字符, 将文本分割为分词。

 

1.3、分词过滤器

 

一旦文本块被转换为分词,ES 将会对每个分词运用分词过滤器

 

(token_filter),这些分词过滤器可以将一个分词作为输入, 然后根据需要进行 修改,添加或者是删除。最为有用的和常用的分词过滤器是小写分词过滤器,它 将输人的分词变为小写,确保在搜索词条 “mysql” 的时候,可以发现关于 “MySql” 的数据。

 

分词可以经过多于 1 个的分词过滤器,每个过滤器对分词进行不同的操 作,将数据塑造为最佳的形式,便于之后的索引。

 

1.4、分词索引

 

当分词经历了零个或者多个分词过滤器,它们将被发送到 Lucene 进行文档 的索引。这些分词存入倒排索引。

 

1.5、分析器

 

所有这些不同的部分,组成了一个分析器( analyzer ), 它可以定义为零个或多 个字符过滤器、1 个分词器、零个或多个分词过滤器。

 

Elasticsearch 中提供了很多预定义的分析器。我们可以直接使用它们而无须构建自己的分析器。

 

2、配置分析器

 

文本分词会发生在两个地方:

创建索引
当索引文档字符类型为 text 时,在建立索引时将会对该字段进行分词;
搜索
当对一个 text 类型的字段进行全文检索时,会对用户输入的文本进行分词。

这两个地方都可以对分词进行配置。

 

2.1、创建索引时

 

ES 将按照下面顺序来确定使用哪个分词器:

先判断字段是否有设置分词器,如果有,则使用字段属性上的分词器设置;
如果设置了 analysis.analyzer.default,则使用该设置的分词器;
如果上面两个都未设置,则使用默认的 standard 分词器

2.1.1、设置索引默认分词器

 

PUT /my_index
{
  "settings": {
    "analysis": {
      "analyzer": {
        "default": {
          "type": "simple"
        }
      }
    }
  }
}

 

response :

 

{
  "acknowledged" : true,
  "shards_acknowledged" : true,
  "index" : "test"
}

 

还可以为索引配置内置分词器,并修改内置的部分选项修改它的行为

 

DELETE /my_index
PUT /my_index
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "type": "standard",
          "stopwords": ["the", "a", "an", "this", "is"]
        }
      }
    }
  }
}

 

2.1.2、为字段指定内置分词器

 

DELETE /my_index
PUT /my_index
{
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "analyzer": "standard",
        "search_analyzer": "simple"
      }
    }
  }
}

 

还可以自定义分词器

 

DELETE /my_index
PUT /my_index
{
  "settings": {
    "analysis": {
      "analyzer": {
        "std_english": {
          "type": "standard",
          "stopwords": "_english_"
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "my_text": {
        "type": "text",
        "analyzer": "standard",
        "fields": {
          "english": {
            "type": "text",
            "analyzer": "std_english"
          }
        }
      }
    }
  }
}

 

我们首先,在索引 my_index 中配置了一个分析器 std_english,std_english 中使用了内置分析器 standard,并将 standard 的停止词模式改为英语模式_english_
(缺省是没有的),对字段 my_text 配置为多数据类型,分别使用了两 种分析器,standard 和 std_english。

 

POST /my_index/_analyze
{
  "field": "my_text",
  "text": "There is an old man"
}

 

response:

 

{
  "tokens" : [
    {
      "token" : "there",
      "start_offset" : 0,
      "end_offset" : 5,
      "type" : "<ALPHANUM>",
      "position" : 0
    },
    {
      "token" : "is",
      "start_offset" : 6,
      "end_offset" : 8,
      "type" : "<ALPHANUM>",
      "position" : 1
    },
    {
      "token" : "an",
      "start_offset" : 9,
      "end_offset" : 11,
      "type" : "<ALPHANUM>",
      "position" : 2
    },
    {
      "token" : "old",
      "start_offset" : 12,
      "end_offset" : 15,
      "type" : "<ALPHANUM>",
      "position" : 3
    },
    {
      "token" : "man",
      "start_offset" : 16,
      "end_offset" : 19,
      "type" : "<ALPHANUM>",
      "position" : 4
    }
  ]
}

 

POST /my_index/_analyze
{
  "field": "my_text.english",
  "text": "There is an old man"
}

 

response:

 

{
  "tokens" : [
    {
      "token" : "old",
      "start_offset" : 12,
      "end_offset" : 15,
      "type" : "<ALPHANUM>",
      "position" : 3
    },
    {
      "token" : "man",
      "start_offset" : 16,
      "end_offset" : 19,
      "type" : "<ALPHANUM>",
      "position" : 4
    }
  ]
}

 

通过上面两个例子看出来 There is an 经过分析之后,被删除了,而standard没有被删除,这是因为 my_text.english 单独配置单独的停止词。

 

2.2、文档搜索时

 

文档搜索时使用的分析器有一点复杂,它依次从如下参数中如果查找文档分析器,如果都没有设置则使用 standard 分析器:

1、搜索时指定 analyzer 参数
2、创建索引时字段指定的 search_analyzer 属性
3、创建索引时字段指定的 analyzer 属性
4、创建索引时 setting 里指定的 analysis.analyzer.default_search
5、如果都没有设置则使用 standard 分析器

2.2.1、搜索时指定 analyzer 查询参数

 

GET /my_index/_search
{
  "query": {
    "match": {
      "message": {
        "query": "Quick foxes",
        "analyzer": "stop"
      }
    }
  }
}

 

2.2.2、创建索引时指定字段的 analyzer 和 seach_analyzer

 

PUT /my_index
{
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "analyzer": "whitespace",
        "search_analyzer": "simple"
      }
    }
  }
}

 

2.2.3、创建索引时指定索引的默认搜索分词器

 

analysis.analyzer.default_search

 

PUT /my_index
{
  "settings": {
    "analysis": {
      "analyzer": {
        "default": {
          "type": "simple"
        },
        "default_seach": {
          "type": "whitespace"
        }
      }
    }
  }
}

 

3、内置分析器

 

前面说过,每个被分析字段经过一系列的处理步骤:

 

1、字符过滤—使用字符过滤器转变字符。

 

2、文本切分为分词—将文本切分为单个或多个分词。

 

3、分词过滤—使用分词过滤器转变每个分词。

 

每个分析器基本上都要包含上面三个步骤至少一个。其中字符过滤器可以为 0 个,也可以为多个,分词器则必须,但是也只能有一个,分词过滤器可以为 0 个,也可以为多个。

 

Elasticsearch 已经为我们内置了很多的字符过滤器、分词器和分词过滤器, 以及分析器。不过常用的就是那幺几个。

 

3.1、字符过滤器(Character filters)

 

字符过滤器种类不多, elasticearch 只提供了三种字符过滤器

 

3.1.1、HTML 字符过滤器(HTML Strip Char Filter)

 

从文本中去除 HTML 元素。

 

html_strip

 

POST _analyze
{
  "tokenizer": "keyword",
  "char_filter": ["html_strip"],
  "text": "<p>I'm your <b>brother</b>!</p>"
}

 

response:

 

{
  "tokens" : [
    {
      "token" : """
I'm your brother!
""",
      "start_offset" : 0,
      "end_offset" : 36,
      "type" : "word",
      "position" : 0
    }
  ]
}

 

3.1.2、映射字符过滤器(Mapping Char Filter)

 

接收键值的映射,每当遇到与键相同的字符串时,它就用该键关联的值替换它们。

 

我们自定义一个分析器,其内的分词器使用关键字分词器,字符 过滤器则是自定制的,将字符中的 “土豆哪里去挖” 替换为 666,”一挖一麻袋” 替换为 888。

 

PUT /my_index
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "tokenizer": "keyword",
          "char_filter": [
            "my_char_filter"
          ]
        }
      },
      "char_filter": {
        "my_char_filter": {
          "type": "mapping",
          "mappings": [
            "土豆哪里去挖 => 666",
            "一挖一麻袋 => 888"
          ]
        }
      }
    }
  }
}

 

POST /my_index/_analyze
{
  "analyzer": "my_analyzer",
  "text": " 土豆哪里去挖,土豆山里去挖,一挖一麻袋"
}

 

response:

 

{
  "tokens" : [
    {
      "token" : " 666,土豆山里去挖,888",
      "start_offset" : 0,
      "end_offset" : 20,
      "type" : "word",
      "position" : 0
    }
  ]
}

 

3.1.3、模式替换过滤器(Pattern Replace Char Filter)

 

POST _analyze
{
  "analyzer": "standard",
  "text": "My phone number is 123-456-789"
}

 

这样分词,会导致 123-456-789 被分为 123 456 789,但是我们希望 123-456-789 是一个整体,可以使用模式替换过滤器,替换掉“-”

 

PUT /my_index
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "tokenizer": "standard",
          "char_filter": [
            "my_char_filter"
          ]
        }
      },
      "char_filter": {
        "my_char_filter": {
          "type": "pattern_replace",
          "pattern": """(\d+)-(?=\d)""",
          "replacement": "$1_"
        }
      }
    }
  }
}

 

POST /my_index/_analyze
{
  "analyzer": "my_analyzer",
  "text": "My phone number is 123-456-789"
}

 

response

 

{
  "tokens" : [
    {
      "token" : "My",
      "start_offset" : 0,
      "end_offset" : 2,
      "type" : "<ALPHANUM>",
      "position" : 0
    },
    {
      "token" : "credit",
      "start_offset" : 3,
      "end_offset" : 9,
      "type" : "<ALPHANUM>",
      "position" : 1
    },
    {
      "token" : "card",
      "start_offset" : 10,
      "end_offset" : 14,
      "type" : "<ALPHANUM>",
      "position" : 2
    },
    {
      "token" : "is",
      "start_offset" : 15,
      "end_offset" : 17,
      "type" : "<ALPHANUM>",
      "position" : 3
    },
    {
      "token" : "123_456_789",
      "start_offset" : 18,
      "end_offset" : 29,
      "type" : "<NUM>",
      "position" : 4
    }
  ]
}

 

3.2、分词器(Tokenizer)

 

3.2.1、标准分词器(standard)

 

标准分词器( standard tokenizer) 是一个基于语法的分词器,对于大多数欧洲

 

语言来说是不错的。它还处理了 Unicode 文本的切分。它也移除了逗号和句号这样的标点符号

 

3.2.2、关键词分词器(keyword)

 

关键词分词器( keyword tokenizer )是-种简单的分词器,将整个文本作为单个的分词,提供给分词过滤器。只想应用分词过滤器,而不做任何分词操作时,它可能非常有用。

 

POST _analyze
{
  "analyzer": "keyword",
  "text": "My credit-- card is 123-456-789"
}

 

response

 

{
  "tokens" : [
    {
      "token" : "My credit-- card is 123-456-789",
      "start_offset" : 0,
      "end_offset" : 31,
      "type" : "word",
      "position" : 0
    }
  ]
}

 

3.2.3、字母分词器(letter)

 

字母分词器根据非字母的符号,将文本切分成分词。例如,对于句子

 

“Hi,there.”分词是 Hi 和 there,

 

因为逗号、空格和句号都不是字母: ‘Hi, there. ‘分词是 Hi 和 there。

 

3.2.4、小写分词器(lowercase)

 

小写分词器( lowercase tokenizer)结合了常规的字母分词器和小写分词过滤

 

器(如你所想,它将整个分词转化为小写)的行为。通过 1 个单独的分词器来实现 的主要原因是,2 次进行两项操作会获得更好的性能。

 

‘Hi, there.’分词是 hi 和 there。

 

3.2.5、空白分词器(whitespace)

 

空白分词器( whitespace tokenizer )通过空白来分隔不同的分词,空白包括空

 

格、制表符、换行等。请注意,这种分词器不会删除任何标点符号。

 

‘Hi, there. ‘分词是 Hi,和 there.

 

3.2.6、模式分词器(pattern)

 

模式分词器( patterm tokenizer)允许指定一个任 意的模式,将文本切分为分

 

词。被指定的模式应该匹配间隔符号。例如,可以创建一个定制分析器,它在出 现文本“. -.”的地方将分词断开。

 

3.2.7、UAX URL 电子邮件分词器(uax_url_email)

 

3.2.8、路径层次分词器(path_hierarchy)

 

路径层次分词器( path hierarchy tokenizer )允许以特定的方式索引文件系统

 

的路径,这样在搜索时,共享同样路径的文件将被作为结果返回。

 

3.3、分词过滤器(Token filters)

 

3.3.1、标准分词过滤器(standard)

 

不要认为标准分词过滤器(standard token filter)进行了什幺复杂的计算,实际上它什幺事情也没做。

 

3.3.2、小写分词过滤器(lowercase)

 

小写分词过滤器( lowercase token filter)只是做了这件事:将任何经过的分词

 

转换为小写。这应该非常简单也易于理解。

 

3.3.3、长度分词过滤器(length)

 

长度分词过滤器(length token filter)将长度超出最短和最长限制范围的单词

 

过滤掉。举个例子,如果将 min 设置为 2,并将 max 设置为 8,任何小于 2 个字符和任何大于 8 个字符的分词将会被移除。

 

3.3.4、停用词分词过滤器(stop)

 

停用词分词过滤器(stop token fite)将停用词从分词流中移除。对于英文而言,

 

这意味着停用词列表中的所有分词都将会被完全移除。用户也可以为这个过滤器指定一个待移除单词的列表。

 

什幺是停用词?

 

停用词是指在信息检索中,为节省存储空间和提高搜索效率,在处理自然语 言数据(或文本)之前或之后会自动过滤掉某些字或词,这些字或词即被称为 Stop Words(停用词)

 

停用词(Stop Words)大致可分为如下两类:

使用十分广泛,甚至是过于频繁的一些单词。比如英文的“i”、“is”、 “what”,中文的“我”、“就”之类词几乎在每个文档上均会出现
文本中出现频率很高,但实际意义又不大的词。这一类主要包括了语气 助词、副词、介词、连词等,通常自身并无明确意义,只有将其放入一个完整的 句子中才有一定作用的词语。如常见的“的”、“在”、“和”、“接着”之类

3.3.5、截断分词过滤器、修剪分词过滤器和限制分词数量过滤器

 

下面 3 个分词过滤器,通过某种方式限制分词流

截断分词过滤器(truncate token filter)允许你通过定制配置中的 length 参数,截断超过一定长度的分词。默认截断多于 10 个字符的部分。
修剪分词过滤器(trim token filter) 删除 1 个分词中的所有空白部分。例如, 分词” foo “将被转变为分词 foo。
限制分词数量分词过滤器(limit token count token filter)限制了某个字段可包含分词的最大数量。例如,如果创建了一个定制的分词数量过滤器,限制是 8, 那幺分词流中只有前 8 个分词会被索引。这个设置使用 max_token_count 参数, 默认是 1 (只有 1 个分词会被索引)。

3.4、常用内置分析器

 

3.4.1、标准分析器

 

当没有指定分析器的时候,标准分析器( standardanalyzer)是文本的默认分析

 

器。它综合了对大多欧洲语言来说合理的默认模块,它没有字符过滤器,包括标 准分词器、小写转换分词过滤器和停用词分词过滤器(默认为_none_,也就是不去除停止词)。这里只需要记住,如果不为某个字段指定分析器,那幺该字段就 会使用标准分析器。可配置的参数如下:

max_token_length,默认值 255,表示词项最大长度,超过这个长度将按该 长度分为多个词项
stopwords,默认值_none_,表示分析器使用的停止词数组,可使用内置停 止词列表,比如_english_等
stopwords_path 停止词文件路径

3.4.2、简单分析器

 

简单分析器(simple analyzer)就是那幺简单!它只使用了小写转换分词器,这

 

意味着在非字母处进行分词,并将分词自动转变为小写。这个分析器对于亚洲语言来说效果不佳,因为亚洲语言不是根据空白来分词,所以请仅仅针对欧洲语言使用它。

 

3.4.3、空白分析器

 

空白分析器(whitespace analyzer)什幺事情都不做,只是根据空白将文本切分

 

为若干分词。

 

3.4.4、停用词分析器

 

停用词分析器(stop analyzer) 和建单分析器的行为很像,只是在分词流中额外的过滤了停用词

 

3.4.5、关键词分析器

 

关键词分析器(keyword analyzer)将整个字段当做一个单独的分词

 

3.4.6、模式分析器

 

模板分析器(pattern analyzer)允许你指定一个分词切分的模式。 但是,由

 

于可能无论如何都要指定模式,通常更有意义的做法是使用定制分析器,组合现 有的模式分词器和所需的分词过滤器

 

3.4.7、雪球分析器

 

雪球分析器(snowball analyzer)除了使用标准的分词器和分词过滤器(和标

 

准分析器一样),也使用了小写分词过滤器和停用词过滤器。它还使用了雪球词干 器对文本进行词干提取。

 

4、自定义分析器

 

业务需求如下:

 

去除所有的 HTML 标签

 

将 & 替换成 and ,使用一个自定义的 mapping 字符过滤器

 

1)自定义 mapping 类型的字符过滤器

 

2)自定义一个停止词 分词过滤器

 

3)组装分析器

 

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

 

POST /my_index/_analyze
{
  "analyzer": "my_analyzer",
  "text": ["I giao do not want to giao work,play"]
}

 

response:

 

{
  "tokens" : [
    {
      "token" : "i",
      "start_offset" : 0,
      "end_offset" : 1,
      "type" : "<ALPHANUM>",
      "position" : 0
    },
    {
      "token" : "do",
      "start_offset" : 7,
      "end_offset" : 9,
      "type" : "<ALPHANUM>",
      "position" : 2
    },
    {
      "token" : "not",
      "start_offset" : 10,
      "end_offset" : 13,
      "type" : "<ALPHANUM>",
      "position" : 3
    },
    {
      "token" : "want",
      "start_offset" : 14,
      "end_offset" : 18,
      "type" : "<ALPHANUM>",
      "position" : 4
    },
    {
      "token" : "to",
      "start_offset" : 19,
      "end_offset" : 21,
      "type" : "<ALPHANUM>",
      "position" : 5
    },
    {
      "token" : "work",
      "start_offset" : 27,
      "end_offset" : 31,
      "type" : "<ALPHANUM>",
      "position" : 7
    }
  ]
}

 

5、中文分析器

 

上面的分析器对中文的支持不行,这里使用比较有名的 ik 作为这次的中文分析器。

 

IK 分词器有两种分词效果,一种是 ik_max_word(最大分词)和 ik_smart(最 小分词)

ik_max_word: 会将文本做最细粒度的拆分,比如会将“马克思主义思想”拆分为“马克思主义,马克思,马克,思想,主义等等,会穷尽各种可能的组合;
ik_smart: 会做最粗粒度的拆分,比如会将“马克思主义思想”拆分为 “马克思主义,思想”。

使用方式和一般的分析器没有什幺差别

 

POST _analyze
{
  "analyzer": "ik_max_word",
  "text": ["马克思主义思想"]
}

 

response:

 

{
  "tokens" : [
    {
      "token" : "马克思主义",
      "start_offset" : 0,
      "end_offset" : 5,
      "type" : "CN_WORD",
      "position" : 0
    },
    {
      "token" : "马克思",
      "start_offset" : 0,
      "end_offset" : 3,
      "type" : "CN_WORD",
      "position" : 1
    },
    {
      "token" : "马克",
      "start_offset" : 0,
      "end_offset" : 2,
      "type" : "CN_WORD",
      "position" : 2
    },
    {
      "token" : "思",
      "start_offset" : 2,
      "end_offset" : 3,
      "type" : "CN_CHAR",
      "position" : 3
    },
    {
      "token" : "主义",
      "start_offset" : 3,
      "end_offset" : 5,
      "type" : "CN_WORD",
      "position" : 4
    },
    {
      "token" : "思想",
      "start_offset" : 5,
      "end_offset" : 7,
      "type" : "CN_WORD",
      "position" : 5
    }
  ]
}

Be First to Comment

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注