Elasticsearch-56-前缀搜索 通配符搜索 正则搜索

准备工作

首先我们先手动建立一个index,再添加几条数据.
创建index

1
2
3
4
5
6
7
8
9
10
11
12
PUT /my_index
{
"mappings": {
"my_type": {
"properties": {
"title":{
"type": "keyword"
}
}
}
}
}

添加几条测试数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
POST /my_index/my_type/1
{
"title":"C3D0-KD345"
}

POST /my_index/my_type/2
{
"title":"C3K5-DFG65"
}

POST /my_index/my_type/3
{
"title":"C4I8-UI365"
}

前缀搜索

C3D0-KD345
C3K5-DFG65
C4I8-UI365
上面我们添加了title是这几个的数据,然后我现在要搜索以C3开头的数据,那么就是要搜索id是1和2的这两条数据,然后看下语法

1
2
3
4
5
6
7
8
9
10
GET /my_index/my_type/_search
{
"query": {
"prefix": {
"title": { // 要搜索的filed
"value": "C3" // 前缀的值
}
}
}
}

返回值:

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
32
33
{
"took": 10,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 2,
"max_score": 1,
"hits": [
{
"_index": "my_index",
"_type": "my_type",
"_id": "2",
"_score": 1,
"_source": {
"title": "C3K5-DFG65"
}
},
{
"_index": "my_index",
"_type": "my_type",
"_id": "1",
"_score": 1,
"_source": {
"title": "C3D0-KD345"
}
}
]
}
}

返回结果就是id是1和2的两个document

前缀搜索的原理

看下上面的返回值两个结果的_score都是1,prefix query是不计算relevance score的,与prefix filter唯一的区别就是,filter会cache bitset
前缀搜索会去扫描整个倒排索引,前缀越短,要处理的doc就越多,性能越差,应该尽可能的用长前缀搜索

举个例子,现在有3个document,内容分别是:
C3-D0-KD345
C3-K5-DFG65
C4-I8-UI365
注意,和上面添加的数据是不一样的.

如果我们使用match全文检索的话,每个字符串是都需要被分词.结果是
c3
d0
kd345
k5
dfg65
c4
i8
ui365
被分成了这几个词,这时候我们去查询c3, 去扫描倒排索引,一旦扫描到c3,就可以停了,已经拿到了所有包含c3的document list了,所以说match的性能往往是很高的

如果不分词,去使用前缀搜索呢,还是查询C3,先找到了C3-D0-KD345,然后还是要继续往下搜,因为后面还可能有其他的前缀是c3的字符串,扫描到了一个前缀匹配的term,不能停,必须继续搜索,直到扫描完整个的倒排索引,才能结束.所以,前缀搜索的性能是很差的

那为什么不使用match搜索,而用前缀搜索呢,因为实际的场景中,可能有些场景是全文检索解决不了的.
再举一个例子,比如说,有以下3个document,值分别是:
C3D0-KD345
C3K5-DFG65
C4I8-UI365
这时候,分词的结果可能就是
c3d0
kd345

这种情况下,用c3 match扫描整个倒排索引,是找不到的. 只能用prefix 前缀搜索

通配符搜索

? 代表任意一个字符
* 代表0个或任意多个字符

比如,表达式是C?K*5,就是以C开头后面可以有任意一个字符,然后接着是K,最后以5结尾
看下语法:

1
2
3
4
5
6
7
8
9
10
GET /my_index/my_type/_search
{
"query": {
"wildcard": {
"title": {
"value": "C?K*5"
}
}
}
}

跟前缀搜索类似,功能更强大,但是性能一样很差,也是需要扫描整个倒排索引

正则搜索

[0-9] 代表指定范围内的数字
[a-z] 代表指定范围内的字母
. 代表一个字符
+ 代表前面的正则表达式可以出现一次或多次

比如正则表达式是 C[0-9].+,就是以C开头后面有一个0-9以内的数字,然后后面可以有多个字符

语法

1
2
3
4
5
6
7
8
GET /my_index/my_type/_search
{
"query": {
"regexp":{
"title":"C[0-9].+"
}
}
}

wildcard和regexp,与prefix原理一致,都会扫描整个索引,性能很差

在实际的应用中,这几种搜索能不用就尽量不用,因为性能很差