ElasticSearch使用edge_ngram进行模糊搜索

当我们需要模糊搜索订单号、手机号的场景下,普通的standard分词器和ik分词器并不能满足我们的要求。因为他们碰到数字和字母不会进行分词,因此我们搜索部分关键词时,不会出现我们预期的结果。

虽然Elasticsearch支持使用正则(regexp)和wildcard进行模糊搜索,但是效率堪忧。为此我们必须找到一个平衡效率和功能的新方法来搜索订单号和手机号这种数据。

1、原理

edge_ngram也是需要分词的,只是分词的方式不一样。你可以规定一个长度,他会在这个长度的基础上进行滚动分词。比如说:java123这段字符串。假设你规定他的滚动长度是3。那么他会分为以下几个词。

1
2
3
4
5
jav
ava
va1
a12
123

对于es来说,这个滚动长度是一个范围。你可以指定一个最小长度和最大长度。但是有一个限制,长度范围不能大于1。比如我们把长度范围设置为2-3。那么java123将会这样分词。

1
2
3
4
5
6
7
8
9
10
11
ja
av
va
a1
12
23
jav
ava
va1
a12
123

这样一来,不论你搜索jav还是ava都能搜索到想要的结果。

2、实操

先在index上定义一个ngram分词器,长度固定数值为3。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
PUT /my_index
{
"settings": {
"analysis": {
"analyzer": {
"my_analyzer": {
"tokenizer": "my_tokenizer"
}
},
"tokenizer": {
"my_tokenizer": {
"type": "ngram",
"min_gram": 3,
"max_gram": 3
}
}
}
}
}

再追加字段到刚刚建好的索引之中。注意,此处如果需要使用自定义的分词器,那么type必须为text类型。

1
2
3
4
5
6
7
8
9
PUT /my_index4/_mapping
{
"properties": {
"title": {
"type": "text",
"analyzer": "my_analyzer"
}
}
}

新增测试数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
PUT /my_index/_doc/1
{
"title": "234568761"
}
PUT /my_index/_doc/2
{
"title": "345678123"
}
PUT /my_index/_doc/3
{
"title": "123456123"
}
PUT /my_index/_doc/4
{
"title": "1212121212"
}
PUT /my_index4/_doc/5
{
"title": "121582684"
}

进行模糊查询

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

GET /my_index/_search
{
"query" : {
"match": {
"title": "123"
}
}
}
//得到预期结果
123456123,345678123

GET /my_index/_search
{
"query" : {
"match": {
"title": "12"
}
}
}
//结果没有任何结果显示,因为我们最小分词长度是3。因此当关键字长度为2时,查不出结果。


GET /my_index/_search
{
"query" : {
"match": {
"title": "121"
}
}
}
//得到预期结果,而且约匹配的排名越前。
1212121212,121582684

3、不适用场景

该单纯使用该分词方法不适合大段文字,只适合小段文字。