一、面临问题
用户在商品查询场景下,通过es查询时,用中文能很好查询到数据,英文或数字必须要写全才能查询到
例:存在商品为:opp0 reno4se 5G全网通智能拍照手机65W闪充正品
1:查询"oppo"可以
2:查询"opp"不行
3:查询"reno"不行
4:查询"reno4se"可以
二、原因
当前的索引为
GET good_test_index
{
"aliases":{
},
"mappings":{
"dynamic":"false",
"properties":{
"create_time":{
"type":"date"
},
"id":{
"type":"keyword"
},
"name":{
"type":"text",
"fields":{
"keyword":{
"type":"keyword",
"ignore_above":256
}
}
}
}
}
}
索引中name字段,未指定分词器
搜集资料和测试,发现当没有指定分词器时,es默认的分词器为standard分词器
官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/8.0/analysis-standard-tokenizer.html
该分词器基于Unicode 文本分段算法,去除空格和标点符号等,但对于连续的英文,则不会分词,从而导致对其不能模糊搜索
POST _analyze
{
"analyzer":"standard",
"text":"测试分词-reno4se"
}
{
"tokens":[
{
"token":"测",
"start_offset":0,
"end_offset":1,
"type":"<IDEOGRAPHIC>",
"position":0
},
{
"token":"试",
"start_offset":1,
"end_offset":2,
"type":"<IDEOGRAPHIC>",
"position":1
},
{
"token":"分",
"start_offset":2,
"end_offset":3,
"type":"<IDEOGRAPHIC>",
"position":2
},
{
"token":"词",
"start_offset":3,
"end_offset":4,
"type":"<IDEOGRAPHIC>",
"position":3
},
{
"token":"reno4se",
"start_offset":5,
"end_offset":12,
"type":"<ALPHANUM>",
"position":4
}
]
}
其中中文是单个拆分,故使用中文搜索时没有该问题,但英文搜索由于分词的原因只有输入完整才能匹配
三、es支持的模糊搜索
1. match_phrase
match_phrase query 首先会把 query 内容分词,分词器可以自定义,同时文档还要满足以下两个条件才会被搜索到:
- 分词后所有词项都要出现在该字段中(相当于 and 操作)。
- 字段中的词项顺序要一致。
2. wildcard:通配符模糊查询
?匹配任意字符
*匹配0个或多个字符
使用wildcard相当于SQL的like,前后都可以拼接*,表示匹配0到多个任意字符加.keyword是要匹配完整的词
GET /test_idx/_search
{
"size":20,
"from":0,
"query":{
"bool":{
"should":[
{
"wildcard":{
"form_name":"*very*"
}
}
]
}
}
}
Wildcard 性能会比较慢。如非必要,尽量避免在开头加通配符 ? 或者 *,这样会明显降低查询性能
3. fuzzy
fuzzy搜索技术 通过编辑距离自动将拼写错误的搜索文本,进行纠正,纠正以后去尝试匹配索引中的数据
编辑距离又称 Levenshtein 距离,是指两个字串之间,由一个转成另一个所需的最少编辑操作次数。许可的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符。fuzzy 查询就是通过计算词项与文档的编辑距离来得到结果的。
但是使用 fuzzy 查询需要消耗的资源比较大,查询效率不高,适用于需要模糊查询的场景。
举例如下,使用fuzzy就行百度一样,你输入个“邓子棋”,也能把“邓紫棋”查出来,有一定的纠错能力
4. 正则
虽然正则很好用,但是在实际使用 regexp 搜索时,我们必须记住如下的事项:
- 避免通配符在前面,比如上面的 .*work。可能以避免使用前导通配符的方式对数据建立索引
- 通常,使用正则表达式可能会很昂贵
{
"query":{
"regexp":{
"name":"[a-z]*"
}
}
}
四、解决问题
从上述可知,wildcard、正则和fuzzy,对于当前场景存在查询效率差的问题,无法采用。
可采用了基于分词器ngram定义自定义分词器,来更好的解决该问题
官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/8.0/analysis-ngram-tokenizer.html
该分词器基于n-ngram,将文本逐字拆分,比如张3w5,会被拆分成张,张3,张3w,3,3w等
可以很好的应对用户模糊搜索的需求
PUT /good_test_index
{
"settings": {
"index.max_ngram_diff":10,
"analysis": {
"analyzer": {
"trigram_analyzer": {
"tokenizer": "trigram_tokenizer",
"filter":[
"lowercase"
]
}
},
"tokenizer": {
"trigram_tokenizer": {
"type": "ngram",
"min_gram": 1,
"max_gram": 3,
"token_chars": [
"letter",
"digit",
"punctuation",
"symbol"
]
}
}
}
},
"mappings": {
"properties": {
"create_time":{
"type":"date"
},
"id":{
"type":"keyword"
},
"name":{
"type": "text",
"analyzer": "trigram_tokenizer"
}
}
}
}
再尝试查询发现能正常通过英文数字进行模糊查询了
咨询方案 获取更多方案详情