Elasticsearch字符串字段通配符搜索:如何实现部分匹配查询
搞定Elasticsearch message字段的部分匹配需求
嘿,我来帮你解决这个部分匹配的问题!你想要让string类型的message字段支持大小写不敏感的子串匹配,比如搜"hello"、"Hello"甚至"o w"都能命中"Hello World"这条记录,对吧?我给你一步步拆解可行的方案:
一、先调整字段映射(核心前提)
你现在只能做完整匹配,大概率是因为message字段被当成了keyword类型(或者旧版ES里的string字段没开启分词)。要实现部分匹配,首先得让字段被正确分词,还要支持大小写不敏感。
1. 重新设置映射(推荐方案,适合能重建索引/重导数据的情况)
如果可以重新创建索引或者把数据重新导一遍,建议把message字段设为text类型,再配个自定义的ngram分词器——这个分词器能把文本拆成连续的短子串,不管你搜什么片段都能命中,还能自动处理大小写:
PUT /你的索引名 { "settings": { "analysis": { "analyzer": { "自定义ngram分词器": { "tokenizer": "自定义ngram分词器规则", "filter": ["lowercase"] // 自动转小写,实现大小写不敏感 } }, "tokenizer": { "自定义ngram分词器规则": { "type": "ngram", "min_gram": 2, // 最小拆分长度,比如2个字符 "max_gram": 10 // 最大拆分长度,按需调整 } } } }, "mappings": { "properties": { "message": { "type": "text", "analyzer": "自定义ngram分词器", "search_analyzer": "standard" // 搜索时用标准分词器,保证搜索词也被正确处理 } } } }
举个例子,"Hello World"会被拆成He、el、ll、lo、o 、 W、Wo、or这些子串,不管你搜"hello"还是"o w",都能匹配到对应的子串,自然就能命中这条记录了。
2. 没法改映射?试试查询层面的临时方案(不推荐,性能差)
如果实在不能动现有索引的映射,那只能用wildcard或者regexp查询,但要注意数据量大的时候会很慢:
GET /你的索引名/_search { "query": { "wildcard": { "message.keyword": { "value": "*hello*", // 前后加*表示包含这个词 "case_insensitive": true // 开启大小写不敏感 } } } }
这个方案是迫不得已才用的,因为wildcard会遍历所有文档,性能开销很大。
二、修改你的查询代码
假设你之前用的是term查询(这就是只能完整匹配的原因),比如Java代码是这样的:
// 之前的完整匹配查询 TermQueryBuilder termQuery = QueryBuilders.termQuery("message", 搜索关键词); SearchSourceBuilder sourceBuilder = new SearchSourceBuilder().query(termQuery);
现在根据不同的映射方案修改:
1. 用了ngram分词器的映射(推荐)
直接换成match查询就行,它会自动用你配置的分词器处理:
// 改成match查询,实现部分匹配 MatchQueryBuilder matchQuery = QueryBuilders.matchQuery("message", 搜索关键词); SearchSourceBuilder sourceBuilder = new SearchSourceBuilder().query(matchQuery);
2. 用wildcard的临时方案
如果没法改映射,就换成wildcard查询:
// 构造包含匹配的wildcard查询,开启大小写不敏感 WildcardQueryBuilder wildcardQuery = QueryBuilders.wildcardQuery("message.keyword", "*" + 搜索关键词 + "*") .caseInsensitive(true); SearchSourceBuilder sourceBuilder = new SearchSourceBuilder().query(wildcardQuery);
一些注意事项
ngram的min_gram和max_gram要按需调,太小会让索引变大,太大就匹配不到短的子串了。- 如果是旧版ES(5.x及以前),string类型要设置
"type": "string", "index": "analyzed"才能开启分词,但更建议升级到新版本用text类型。 - 数据多的时候,一定要优先用分词器的方案,别用wildcard,不然查询会慢到离谱。
内容的提问来源于stack exchange,提问作者Justin
相关产品推荐
相关产品推荐

