Elasticsearch multi_match查询如何仅匹配手机号字符串开头而非中间内容
这个问题核心是因为你用的multi_match的phrase_prefix是针对分词后的文本片段做前缀匹配,而不是整个手机号字符串的开头匹配。默认情况下,phone字段作为text类型会被分词器拆分(比如222-401-8120会被拆成222、401、8120三个词),所以查询40的时候,会匹配到任何分词后以40开头的词(比如401),自然就命中了中间位置带40的手机号。
要解决这个问题,你需要针对手机号的原始完整字符串做前缀匹配,而不是分词后的片段。这里有几个可靠的方案:
方案1:使用prefix查询针对keyword子字段
Elasticsearch默认会给text类型字段生成一个同名的keyword子字段(比如phone.keyword),这个子字段会存储原始的未分词字符串。你可以直接用prefix查询匹配开头为40的手机号:
GET emp_details_1_1/_msearch {"index": "emp_details_1_1"} {"_source":[],"size":0,"min_score":1,"query":{"multi_match":{"query":"40","fields":["ssn"],"type":"phrase_prefix"}}} {"index": "emp_details_1_1"} {"_source":[],"size":0,"min_score":1,"query":{"prefix":{"phone.keyword":"40"}}}
如果是要查询以21开头的手机号,把"40"换成"21"即可。
方案2:使用wildcard查询(效果和prefix类似)
如果你更习惯通配符语法,也可以用wildcard查询:
{"_source":[],"size":0,"min_score":1,"query":{"wildcard":{"phone.keyword":"40*"}}}
方案3:使用正则表达式精准匹配开头
如果需要更严格的匹配(比如确保是字符串绝对开头),可以用regexp查询:
{"_source":[],"size":0,"min_score":1,"query":{"regexp":{"phone.keyword":"^40.*"}}}
注意事项
- 必须使用
phone.keyword而不是phone:因为phone是text类型,分词后会拆分字符串,而phone.keyword存储的是原始未分词的手机号,能保证匹配的是整个字符串的开头。 - 如果你的索引里没有
phone.keyword子字段,可以先执行GET emp_details_1_1/_mapping/field/phone查看字段映射。如果确实没有,需要修改字段映射添加keyword子字段(不过默认是会自动生成的)。
内容的提问来源于stack exchange,提问作者Green
相关产品推荐
相关产品推荐

