前段时间第一次使用es实现全文检索功能,由于第一次使用,所以只想实现类似数据库 LIKE ‘%关键字%’
这样的就可以了。
简单研究了一下es之后,发现将字段类型指定为keyword
或查询字段加上.keyword
,然后再通过*
实例模糊匹配,如下面查询,就可以实现类似LIKE ‘%关键字%’
的搜索。
GET /my_index/_doc/_search
{
"query": {
"query_string": {
"query": "*关键字*",
"fields": [
"field1.keyword",
"field2.keyword",
"field3.keyword",
"field4.keyword"
]
}
}
}
虽然这样达到了想要的结果,但过了一段时间发现es这种查询有性能问题,如果关键字内容越长查询时间越长,例如查询10个字符的关键字,数据量才几w级别的竟然要4、5秒,还不如优化过的sql查询。
实现配置
elasticsearch 版本: 6.3.0
实现原理
将内容按字符分词,然后查询的时候也是按字符匹配,并且还要满足字符的顺序,如:
- 内容:elasticsearch自定义分词器实现模糊搜索。
- 分词后:e, l, a, s, t, i, c, s, e, a, r, c, h, 自, 定, 义, 分, 词, 器, 实, 现, 模, 糊, 搜, 索。
- 搜索关系字:自定义分词器
匹配“自, 定, 义, 分, 词, 器”这个几个字符,并且它们的位置满足:自<定<义<分<词<器,并且相邻两个字符位置差值都是1。
以上关键是最后一步,可以用 match_phrase
这个查询实现。
创建索引时创建自定义分词器
// 创建索引
PUT /my_index
{
"settings": {
"analysis": {
"analyzer": {
// 自定义分词器
"char_analyzer": {
// 使用自定义的token分词器
"tokenizer": "char_tokenizer",
// 转成小写,实现忽略大小写查询,如果需要区分大小写把下面的filter去掉
"filter": "lowercase"
}
},
"tokenizer": {
// 定义一个token分词器,作用是将内容按字符拆分
"char_tokenizer": {
"type": "pattern",
"pattern": "|"
}
}
}
}
}
使用分词器
使用分词器有两种方式,一种是使用动态模板给满足条件的字段指定分词器,另一种就是给指定的字段设置分词器,使用其它一种就好了。反正就是要给需要查询的字段使用上刚刚定义的分词器。
// 使用动态模板,指定自定义分词器
PUT my_index/_doc/_mapping
{
"_doc": {
"dynamic": true,
"dynamic_templates": [{
"my_analyze": {
"match_mapping_type": "string",
"mapping": {
"analyzer": "char_analyzer"
}
}
}]
}
}
// 指定字段设置分词器
PUT my_index/_doc/_mapping
{
"_doc": {
"properties": [{
"field1": {
"analyzer": "char_analyzer"
},
"field2": {
"analyzer": "char_analyzer"
}
}]
}
}
关键部分,实例模糊查询
这是最关键的部分
GET /my_index/_doc/_search
{
"query": {
"query_string": {
"query": "关键字",
// [关键]作用:满足条件需要按顺序匹配关键字的每一个字
"type": "phrase",
// [关键]作用:需要匹配到每一个关键字
"operator": "AND",
// [关键]作用:跟上面一样,需要匹配到每一个关键字
"minimum_should_match": "100%",
"fields": [
"field1",
"field2",
"field3",
"field4"
]
}
}
}