如何在ElasticSearch中实现类似LIKE的部分匹配查询?
在Elasticsearch中实现类似SQL LIKE的包含查询
嘿,我来帮你搞定这个Elasticsearch的模糊查询问题!你想要的是类似SELECT * FROM table WHERE firstname LIKE '%ma%'的效果对吧?先说说你之前的尝试为啥没生效,再给你几个实用的解决方案。
为啥你之前的查询没起作用?
咱们逐个看你试的语句:
{"match": {"text": ".*ma.*"}}:match查询是基于分词的全文匹配,它会把.*ma.*当成普通字符串去匹配,而不是正则表达式——Elasticsearch不会把这里的.和*解析成正则符号。{"match": {"text": "*ma*"}}:同样的问题,match不支持通配符语法,*在这里只是普通字符,所以只会匹配包含*ma*的文本,而不是包含ma的文本。{"match":{"text":{"query":"ma","fuzziness":"AUTO","prefix_length":1}}}:这个是模糊匹配,它会找和ma编辑距离相近的词(比如man、mad),但不是包含ma的任意位置的文本——比如Emma里的ma就匹配不到,因为它是在中间位置,模糊匹配是基于整个词的相似度,不是包含关系。
正确的实现方法
根据你的需求,这里有几种靠谱的方案,你可以根据场景选择:
1. 使用wildcard查询(快速实现,适合小数据量)
wildcard查询原生支持通配符*(匹配任意字符)和?(匹配单个字符),注意要针对keyword类型字段查询(或者字段的keyword子字段,默认mapping里text字段都会带一个),因为text字段会被分词,直接查可能得不到预期结果:
{ "query": { "wildcard": { "firstname.keyword": "*ma*" } } }
如果你的firstname字段本身就是keyword类型,直接写firstname就行。
2. 使用query_string查询(支持类似SQL的LIKE语法)
query_string支持更灵活的查询语法,包括通配符,写法更接近SQL的LIKE:
{ "query": { "query_string": { "default_field": "firstname.keyword", "query": "*ma*" } } }
同样,建议用keyword子字段来保证匹配原始文本。
3. 使用ngram分词器(高性能,适合频繁查询)
如果你的查询是高频需求,上面两种方法用*开头会导致Elasticsearch做全索引扫描,数据量大的时候性能很差。这时候可以提前给字段设置ngram分词器,把文本拆成连续的字符片段(比如2个字符一组),这样查询时就能利用索引高效匹配:
第一步:创建索引时配置ngram分析器
{ "settings": { "analysis": { "analyzer": { "ngram_analyzer": { "tokenizer": "ngram_tokenizer" } }, "tokenizer": { "ngram_tokenizer": { "type": "ngram", "min_gram": 2, "max_gram": 2 // 这里拆成2字符的片段,刚好匹配你的"ma"需求 } } } }, "mappings": { "properties": { "firstname": { "type": "text", "analyzer": "ngram_analyzer", // 索引时用ngram拆分 "search_analyzer": "standard" // 查询时用标准分词,避免重复拆分 } } } }
第二步:用普通match查询即可
{ "query": { "match": { "firstname": "ma" } } }
这种方法性能最优,因为索引已经提前把所有可能的2字符片段存好了,查询时直接匹配索引就行。
总结
- 临时查询、数据量小:用
wildcard或query_string - 高频查询、数据量大:提前配置
ngram分词器,用match查询
内容的提问来源于stack exchange,提问作者Baldráni




