在实际工作中,模糊查询(类似 MySQL 的 LIKE '%keyword%')是常见需求。例如,输入“手机”时,希望返回“智能手机”“手机壳”等商品,MySQL 的 LIKE 查询简单直观,但在大规模数据场景下,全表扫描会导致查询耗时激增。
本文带你实现Elasticsearch 全模糊查询,轻松搞定!
使用wildcard词项搜索
wildcard 查询是最直观的实现方式,语法类似 MySQL 的 LIKE '%keyword%',通过 * 通配符匹配字段中的任意子串。
索引数据
准备一些测试数据:
POST _bulk
{"index":{"_index":"post","_id":1}}
{"title":"Elasticsearch入门到精通"}
{"index":{"_index":"post","_id":2}}
{"title":"MySQL数据库优化"}
{"index":{"_index":"post","_id":3}}
{"title":"21天精通MySQL数据库"}
{"index":{"_index":"post","_id":4}}
{"title":"21天精通SQL语言"}
wildcard 查询
要查找标题中包含“MySQL”的文档,可以使用以下 wildcard 查询:
GET post/_search
{
"query": {
"wildcard": {
"title.keyword": {
"value": "*MySQL*"
}
}
}
}
查询结果如下图所示:
image.png
虽然wildcard 查询轻松实现了全模糊查询,但性能极差,因为需要遍历倒排索引中的所有词条,在数据量较大(百万级以上)时,可能导致查询延迟高甚至集群崩溃。
N-gram 分词器
为了解决 wildcard 的性能问题,N-gram 分词器是一个更高效的方案。它在索引时将文本拆分成连续的字符片段(grams),从而支持高效的子串匹配。
例如:对于文本 “你好世界”,使用n-gram 分词器(默认配置)会将其拆分为:[ "你","你好", "好","好世","世","世界","界" ]。
配置 N-gram 分词器
PUT post_v2
{
"settings": {
"analysis": {
"analyzer": {
"ngram_analyzer": {
"tokenizer": "ngram_tokenizer"
}
},
"tokenizer": {
"ngram_tokenizer": {
"type": "ngram",
"min_gram": 1,
"max_gram": 2
}
}
}
},
// title.ngram 字段专门用于模糊查询
"mappings": {
"properties": {
"title": {
"type": "text",
"fields": {
"ngram": {
"type": "text",
"analyzer": "ngram_analyzer"
}
}
}
}
}
}
min_gram 和 max_gram 控制切片长度,详细说明请查看官方文档。
索引数据
使用与之前相同的测试数据:
POST _bulk
{"index":{"_index":"post_v2","_id":1}}
{"title":"Elasticsearch入门到精通"}
{"index":{"_index":"post_v2","_id":2}}
{"title":"MySQL数据库优化"}
{"index":{"_index":"post_v2","_id":3}}
{"title":"21天精通MySQL数据库"}
{"index":{"_index":"post_v2","_id":4}}
{"title":"21天精通SQL语言"}
执行查询
使用match 查询在title.ngram 字段上搜索“MySQL”:
GET post_v2/_search
{
"query": {
"match": {
"title.ngram": "MySQL"
}
}
}
查询结果如下图所示:
image.png
查询结果返回了不符合要求的数据:"21天精通SQL语言",这是因为match查询默认情况下使用 or 逻辑,修改operator = and 即可。
GET post_v2/_search
{
"query": {
"match": {
"title.ngram": {
"query":"MySQL",
"operator": "and"
}
}
}
}
缺点:生成了大量的 n-gram 词条,索引会比原来大很多。
感谢您的阅读!希望这部分内容对您有所启发。如果您觉得有价值,请不吝点赞支持,并在评论区留下您的想法,一起交流学习!别忘了关注我哦!
在实际开发中,您如何实现Elasticsearch的全模糊,欢迎在评论区分享!