300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > ES第十三天-扩展查询-前缀匹配 通配符查询 正则查询 模糊匹配 句子前缀匹配

ES第十三天-扩展查询-前缀匹配 通配符查询 正则查询 模糊匹配 句子前缀匹配

时间:2019-07-18 14:43:59

相关推荐

ES第十三天-扩展查询-前缀匹配 通配符查询 正则查询 模糊匹配 句子前缀匹配

前言

ES的无论什么搜索,对于text类型字段其实都是基于倒排索引去进行搜索的,也就是进行分词后的,因此如果想像传统数据库一样的模糊匹配,一般可以使用它的keyword进行搜索。(keyword不会被分词)

以下的搜索在大型生产环境都不推荐使用。

前缀索引查询

以xx开头的搜索,不计算相关度评分,和filter比,没有bitcache。前缀搜索,尽量把前缀长度设置的更长,性能差,一般大规模产品不使用。(是去倒排索引中去匹配前缀,需要遍历每一个倒排索引才能找到所有匹配的)

语法

GET index/_search{"query": {"prefix": {"title": {"value": "text"}}}}

为了加快前缀搜索速度,可以设置默认的 前缀索引 (空间换时间)

PUT my_index{"mappings": {"properties": {"text": {"type": "text","index_prefixes": {"min_chars":2, "max_chars":4} }}}}

上面这个设置的意思是,把分词后的每个词项的2-4个字符额外进行建立前缀倒排索引,从而提高后续前缀匹配的速度,但是占用空间也是相对变大。

index_prefixes: 默认 “min_chars” : 2, “max_chars” : 5 。

通配符查询

通配符查询类似于正则,但没正则强大,允许对匹配表达式进行通配符占位。

表示匹配任意长度的任意字符

? 表示匹配一个任意字符

[…]则表示匹配括号中列出的字符中的任意一个

[!..]表示不匹配括号中列出的字符中的任意一个

语法

{"query": {"wildcard": {"text": {"value": "eng?ish"}}}}

正则查询

regexp查询的性能可以根据提供的正则表达式而有所不同。为了提高性能,应避免使用通配符模式,如.或 .?+未经前缀或后缀

语法

{"query": {"regexp": {"name": {"value": "[\\s\\S]*nfc[\\s\\S]*","flags": "ALL","max_determinized_states": 10000, #防止正则内存过大的保护措施"rewrite": "constant_score"}}}}

关于参数flags,有几个配置可选:

ALL (Default)

启用所有可选操作符。

COMPLEMENT

启用操作符。可以使用对下面最短的模式进行否定。例如

a~bc # matches ‘adc’ and ‘aec’ but not ‘abc’

INTERVAL

启用<>操作符。可以使用<>匹配数值范围。例如

foo<1-100> # matches ‘foo1’, ‘foo2’ … ‘foo99’, ‘foo100’

foo<01-100> # matches ‘foo01’, ‘foo02’ … ‘foo99’, ‘foo100’

INTERSECTION

启用&操作符,它充当AND操作符。如果左边和右边的模式都匹配,则匹配成功。例如:

aaa.+&.+bbb # matches ‘aaabbb’

ANYSTRING

启用@操作符。您可以使用@来匹配任何整个字符串。

您可以将@操作符与&和~操作符组合起来,创建一个“everything except”逻辑。例如:

@&~(abc.+) # matches everything except terms beginning with ‘abc’

Fuzzy模糊(容错)匹配

场景

1、混淆字符 (box → fox)

2、缺少字符 (black → lack)

3、多出字符 (sic → sick)

4、颠倒次序 (act → cat)

在出现上面情况的时候,我们也希望用户可以搜索到想要的内容,那么这个时候可以使用fuzzy。

语法

以下两种都可以:

1、第一种-手动档

可以手动多指定一些参数,但一般也不建议改动

{"query": {"fuzzy": {"desc": {"value": "quangemneng","fuzziness": 5}}}}

① value:(必需,字符串)

② fuzziness:(可选,字符串)最大误差 并非越大越好, 因为大了虽然召回率高 但是结果不准确

1) 两段文本之间的Damerau-Levenshtein距离是使一个字符串与另一个字符串匹配所需的插入、删除、替换和调换的数量

2) 距离公式:Levenshtein是lucene的,es改进版:Damerau-Levenshtein

3) axe=>aex Levenshtein=2 Damerau-Levenshtein=1

③ max_expansions:可选,整数)匹配的最大词项数量。默认为50。

④ prefix_length:创建扩展时保留不变的开始字符数。默认为0

1)避免在max_expansions参数中使用较高的值,尤其是当prefix_length参数值为时0。max_expansions由于检查的变量数量过多,参数中的高值 可能导致性能不佳。

⑤ transpositions:(可选,布尔值)指示编辑是否包括两个相邻字符的变位(ab→ba)。默认为true。

⑥ rewrite:(可选,字符串)用于重写查询的方法

https://www.elastic.co/cn/blog/found-fuzzy-search#performance-considerations

2、第二种,"自动挡"时代

{"query": {"match": {"desc": {"query": "quangengneng nfc","fuzziness": "AUTO"}}}}

match_phrase_prefix(最简陋的Suggest)

match_phrase_prefix与match_phrase相同,但是它多了一个特性,就是它允许在文本的最后一个词项(term)上的前缀匹配,如果 是一个单词,比如a,它会匹配文档字段所有以a开头的文档,如果是一个短语,比如 “this is ma” ,他会先在倒排索引中做以ma做前缀搜索,然后在匹配到的doc中做match_phrase查询,(网上有的说是先match_phrase,然后再进行前缀搜索, 是不对的)

语法

{"query": {"match_phrase_prefix": {"desc": {"query": "zhichi quangongneng nf","analyzer": "whitespace","max_expansions": 1,"slop": 2,"boost": 1}}}}

参数

analyzer

指定何种分析器来对该短语进行分词处理

max_expansions

限制匹配的最大term数。

一般来讲,前缀匹配是会全索引进行扫描匹配的,为了提高效率,可以进行限制它可以进行扫描的索引的个数,但即使设置为1,也不意味着返回的doc结果只有一个,主要有2点:

1、一个索引可能有多个doc

2、这个限制扫描个数是针对每个分片来说的(每个分片都可以扫描1个),因此也就是说该索引的每个分片扫描的第一个term都可能被匹配上。

boost

用于设置该查询的权重

slop

允许短语间的词项(term)间隔

slop参数告诉match_phrase查询词条相隔多远时仍然能将文档视为匹配, 什么是相隔多远? 意思是说为了让查询和文档匹配你需要移动词条多少次?举个例子:

如果我们输入的是:de zhong shouji hongzhaji

而期望匹配的句子是:shouji zhong de hongzhaji

那么要怎么移动呢?

1、首先要把“shouji”词条向左移动2个词条:

shouji/de zhong shouji hongzhaji

2、接下来在把de向右移动2个词条:

shouji zhong de shouji hongzhaji

这样下来,共需要4次移动,因此slop需要设置为4的时候,输入的才能匹配上。

N-gram-tokenFilter

上面的前缀匹配还是存在性能问题,那有没有相对好一点的方法呢? 我们可以从分词角度出发。

在设置索引的时候,可以进行指定分词器的相关属性,其中有一项是指定fliter,可以通过指定ngram:

{"settings": {"analysis": {"filter": {"2_3_grams": {"type": "ngram","min_gram": 1,"max_gram": 2}},"analyzer": {"my_ngram": {"type":"custom","tokenizer": "standard","filter": [ "2_3_grams" ]}}}},"mappings": {"properties": {"text": {"type": "text","analyzer":"my_ngram","search_analyzer": "standard"}}}}

经过ngram设置的min_gram:1和"max_gram": 3,分析以下语句:

GET _analyze{"tokenizer": "ik_max_word","filter": [ "edge_ngram" ],"text": "reba always"}

会先按最小粒度1进行拆分,也就是拆分出“r”,“e”,“b”,“a”,“a”,“l”,“w”,“a”,“y”,“s”

然后按粒度2拆分,拆分成:“re”,“eb”,“ba”,“al”,“lw”,“wa”,“ay”,“ys”

然后按最大粒度3进行拆分,拆分成:“reb”,“eba”,“alw”,“lwa”,“way”,“ays”

Edge-N-gram-tokenFilter

另外一个filter,根据min_gram和max_gram对分词的开头部分进行拆分。

这个可能更常用一些,因为更多的我们的搜索是从一个词的开头进行部分搜索,而不是中间进行搜索。

GET _analyze{"tokenizer": "ik_max_word","filter": [ "edge_ngram" ],"text": "reba always loves me"}

min_gram =1 “max_gram”: 1

拆分情况:r a l m

min_gram =1 “max_gram”: 2

拆分情况:

r a l m

re al lo me

min_gram =2 “max_gram”: 3

拆分情况:

re al lo me

reb alw lov me

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。