0%

elasticsearch(六)-前缀、通配符、正则、模糊查询

prefix-前缀

前缀查询:以xx开头的搜索,不计算相关度评分,和filter比,没有bitcache。前缀搜索,尽量把前缀长度设置的更长,性能差,因为它会扫描倒排索引整张表,匹配每个term是否包含xx。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
语法:
GET index/_search
{
"query": {
"prefix": {
"title": {
"value": "text"
}
}
}
}

参数:
index_prefixes: 默认 "min_chars" : 2, "max_chars" : 5
1
2
3
4
5
6
7
8
9
10
11
//类似于SQL的左Like查询
GET my_index/_search
{
"query": {
"prefix": {
"text": {
"value": "myword"
}
}
}
}

使用index_prefixes映射,ES会额外建立一个长度在2和5之间索引,在进行前缀匹配的时候效率会有很大的提高

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//设置默认的 启动索引 加快前缀搜索速度
PUT my_index
{
"mappings": {
"properties": {
"text": {
"type": "text",
"index_prefixes": {
"min_chars":2,
"max_chars":4
}
}
}
}
}

wildcard-通配符

通配符:通配符运算符是匹配一个或多个字符的占位符。例如,*通配符运算符匹配零个或多个字符。可以将通配符运算符与其他字符结合使用以创建通配符模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//  "name" : "xiaomi nfc phone"
//通配符匹配
GET product/_search
{
"query": {
"wildcard": {
"name": { //匹配倒排索引表中的term词项
"value": "xia?mi"
}
}
}
}

输出结果:
能查出 "name" : "xiaomi nfc phone" 的数据
1
2
3
4
5
6
7
8
9
10
11
12
13
GET product/_search
{
"query": {
"wildcard": {
"name.keyword": { //全量匹配内容
"value": "xia?mi"
}
}
}
}

输出结果:
不能查出数据

regexp-正则

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

1
2
3
4
5
6
7
8
9
10
11
12
//"name" : "xiaomi nfc phone"  正则匹配
GET product/_search
{
"query": {
"regexp": {
"name": {
"value": "[\\s\\S]*nfc[\\s\\S]*", // \\s\\S表示任意字符
"flags": "ALL"
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
//"desc" : "xiaomi nfc 2020-05-20 phone"
GET product/_search
{
"query": {
"regexp": {
"desc": {
"value": ".*2020-05-20.*",
"flags": "ALL"
}
}
}
}

无法匹配到上文中的注释数据,原因在于默认使用了standard,会将日期拆成【2020】、【05】、【20】三个词汇,如果想要匹配,需要使用desc.keyword进行全文匹配,但是这样性能十分低下。

而IK分词器很好的对日期进行了分词词项【2020-05-20】,对该索引指定IK分词器(需重建索引),即可查询到结果。

1
2
3
4
5
6
7
8
9
10
11
12
PUT product
{
"mappings": {
"properties": {
"desc": {
"type": "text",
"analyzer": "ik_max_word", //倒排索引分词
"search_analyzer": "ik_max_word" //搜索分词
}
}
}
}

fuzzy-混淆模糊查询

混淆字符 (box → fox) 缺少字符 (black → lack)

多出字符 (sic → sick) 颠倒次序 (act → cat)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
语法:
GET /_search
{
"query": {
"fuzzy": {
"desc": {
"value": "keyword"
}
}
}
}

参数:
value:(必需,字符串)
fuzziness:(可选,字符串)最大误差(距离) 并非越大越好, 召回率高 但是结果不准确

距离:两段文本之间的Damerau-Levenshtein距离是指一个字符串需要经过多少次操作之后才能变成另一个字符串。

距离公式:Levenshtein是lucene的,es改进版:Damerau-Levenshtein,axe=>aex Levenshtein=2 ,而Damerau-Levenshtein=1

1
2
3
4
5
6
7
8
9
10
11
12
//案例 使用fuzzy
GET /product/_search
{
"query": {
"fuzzy": {
"desc": {
"value": "xioami", //匹配 xiaomi
"fuzziness": 5
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
//案例 使用match
GET /product/_search
{
"query": {
"match": {
"desc": {
"query": "quangengneng nfc",
"fuzziness": "AUTO"
}
}
}
}

match_phrase_prefix-短语前缀

match_phrase_prefix与match_phrase相同,但是它多了一个特性,就是它允许在文本的最后一个词项(term)上的前缀匹配。

如果 是一个单词,比如a,它会匹配文档字段所有以a开头的文档,如果是一个短语,比如 “this is ma” ,他会先在倒排索引中做以ma做前缀搜索,然后在匹配到的doc中做match_phrase查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
语法:
GET /index/_search
{
"query": {
"match_phrase_prefix": {
"fieldName": {
"query": "text"
}
}
}
}

参数
analyzer 指定何种分析器来对该短语进行分词处理
max_expansions 限制匹配到的最大词项,每个分片中匹配的结果达到max_expansions值后停止遍历倒排索引表
boost 用于设置该查询的权重
slop:允许短语间的词项(term)间隔,指为了让查询和文档匹配需要移动term多少(slop)次
1
2
3
4
5
6
7
8
9
10
11
//案例  匹配 "desc" : "zhichi nfc"   注意!无法匹配 "zhichi aaa nfc"
GET /product/_search
{
"query": {
"match_phrase_prefix": {
"desc": {
"query": "zhichi nf", //先进行nf 前缀匹配,再进行match_phrase(zhichi)词项匹配
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//案例 可以匹配到:"zhichi aaa nfc"
GET /product/_search
{
"query": {
"match_phrase_prefix": {
"desc": {
"query": "zhichi nf",
"analyzer": "whitespace",
"max_expansions": 1,
"slop": 1, //配置slop,允许移动一次term匹配结果
"boost": 1
}
}
}
}

N-gram:token filter

1
2
3
4
5
6
edge_ngram:是从第一个字符开始,按照步长,进行分词,适合前缀匹配场景,比如:订单号,手机号,邮政编码的检索
ngram:是从每一个字符开始,按照步长,进行分词,适合前缀中缀检索

参数:
min_gram =1 最小步长 默认值 1
max_gram =1 最大步长 默认值 1
1
2
3
4
5
6
7
8
9
10
11
【edge_ngram】 拆词: "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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//创建索引,设置分词器和token filter
PUT my_index
{
"settings": {
"analysis": {
"filter": {
"2_3_edge_ngram": {
"type": "edge_ngram",
"min_gram": 2,
"max_gram": 3
}
},
"analyzer": {
"my_edge_ngram": {
"type":"custom",
"tokenizer": "standard",
"filter": [ "2_3_edge_ngram" ]
}
}
}
},
"mappings": {
"properties": {
"text": {
"type": "text",
"analyzer":"my_edge_ngram",
"search_analyzer": "standard"
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
GET /my_index/_analyze
{
"analyzer": "my_edge_ngram"
, "text": ["my english"]
}

输出结果:
my、en、eng

如果分词器把edge_ngram换成ngram,则输出结果如下:
my、en、ng、gl、li、is、sh、eng、ngl、gli、lis、ish
1
2
3
4
5
6
7
8
9
//匹配 "text": "my english is good"
GET /my_index/_search
{
"query": {
"match_phrase": {
"text": "my eng is goo"
}
}
}