词条(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