Elasticsearch带通配符短语的query_string返回意外结果排查
背景
我正在处理一个存储企业描述的大型Elasticsearch索引,需要检索同时提及区块链技术和计算机病毒的文档。
查询语句
使用的query_string查询如下:
GET company_descriptions/_search { "query": { "query_string": { "query": "(\"Blockchain\" or \"block-chain\" OR \"Blockchain?\" OR \"Block-chain?\" OR \"Distributed Ledger\" OR \"Distributed Ledger?\") AND (\"viruses\" OR \"virus\")", "fields": ["services.no_case_sensitive", "description.no_case_sensitive"] } } }
查询逻辑是:匹配包含任意区块链相关短语(含带通配符的变体),并且包含病毒相关词汇的文档。
问题现象
查询返回了如下文档,但该文档仅包含viruses,完全没有区块链相关词汇:
{ "_index": "company_descriptions", "_id": "123456", "_score": 20.032307, "_source": { "description": "To relieve needs of persons who are HIV positive or are suffering from aids or blood borne viruses and their families and/or carers and to advance the education of the public in the treatment and prevention of HIV and Aids and blood borne viruses.", "services": null } }
我的推理是:
- 查询的区块链相关部分(AND之前)无匹配,结果为False
- 病毒相关部分(AND之后)匹配,结果为True
- 由于是AND逻辑,整体结果应为False,该文档不应被返回
但实际结果与之相悖,请问问题出在哪里?
补充信息
索引映射
{ "properties": { "description": { "type": "text", "fields": { "no_case_sensitive": { "type": "text", "analyzer": "NO_CASE_SENSITIVE", "search_analyzer": "NO_CASE_SENSITIVE" }, "case_sensitive": { "type": "text", "analyzer": "CASE_SENSITIVE", "search_analyzer": "CASE_SENSITIVE" } } }, "services": { "type": "text", "fields": { "no_case_sensitive": { "type": "text", "analyzer": "NO_CASE_SENSITIVE", "search_analyzer": "NO_CASE_SENSITIVE" }, "case_sensitive": { "type": "text", "analyzer": "CASE_SENSITIVE", "search_analyzer": "CASE_SENSITIVE" } } } } }
分析器配置
{ "settings": { "analysis": { "analyzer": { "NO_CASE_SENSITIVE": { "type": "custom", "stopwords": [], "filter": [ "lowercase" ], "tokenizer": "standard" }, "CASE_SENSITIVE": { "type": "custom", "stopwords": [], "filter": [], "tokenizer": "standard" } } } } }
问题原因
核心问题出在**query_string对带通配符的短语的解析逻辑**以及多字段查询的行为上:
短语查询不支持通配符
Elasticsearch的短语查询(带引号的查询)本身不支持包含通配符。你在查询中使用的"Blockchain?"、"Block-chain?"、"Distributed Ledger?"这类带通配符的“短语”,会被query_string解析为无效的查询条件,进而被Elasticsearch忽略。无效条件被忽略后,AND逻辑被破坏
当区块链相关的多个查询条件中,有效条件(不带通配符的短语如"Blockchain")无匹配,而带通配符的短语条件被忽略时,整个区块链查询分支的逻辑会被简化为“无有效条件”,此时Elasticsearch可能将该分支视为匹配所有文档(这是query_string的隐含特性:当查询条件为空或无效时,默认匹配所有)。
此时原查询的逻辑被意外转化为:(匹配所有文档) AND (病毒相关词匹配),自然会返回所有包含病毒相关词的文档,包括你提到的这篇无区块链内容的文档。
解决方案
1. 拆分通配符查询与短语查询
将带通配符的词单独作为词项通配符查询,而非短语查询,同时保留不带通配符的短语查询:
GET company_descriptions/_search { "query": { "query_string": { "query": "(Blockchain OR block-chain OR Blockchain? OR block-chain? OR \"Distributed Ledger\" OR distributed ledger?) AND (viruses OR virus)", "fields": ["services.no_case_sensitive", "description.no_case_sensitive"] } } }
注意去掉通配符词的引号,让其作为词项通配符而非短语查询。
2. 使用bool查询替代query_string(推荐)
bool查询的逻辑更清晰,能避免query_string的解析歧义,明确控制每个条件的匹配规则:
GET company_descriptions/_search { "query": { "bool": { "must": [ // 匹配任意区块链相关内容 { "bool": { "should": [ {"match_phrase": {"services.no_case_sensitive": "Blockchain"}}, {"match_phrase": {"description.no_case_sensitive": "Blockchain"}}, {"match_phrase": {"services.no_case_sensitive": "block-chain"}}, {"match_phrase": {"description.no_case_sensitive": "block-chain"}}, {"wildcard": {"services.no_case_sensitive": "blockchain?"}}, {"wildcard": {"description.no_case_sensitive": "blockchain?"}}, {"wildcard": {"services.no_case_sensitive": "block-chain?"}}, {"wildcard": {"description.no_case_sensitive": "block-chain?"}}, {"match_phrase": {"services.no_case_sensitive": "Distributed Ledger"}}, {"match_phrase": {"description.no_case_sensitive": "Distributed Ledger"}}, // 处理带通配符的Distributed Ledger变体 {"bool": { "must": [ {"match": {"services.no_case_sensitive": "distributed"}}, {"wildcard": {"services.no_case_sensitive": "ledger?"}} ] }}, {"bool": { "must": [ {"match": {"description.no_case_sensitive": "distributed"}}, {"wildcard": {"description.no_case_sensitive": "ledger?"}} ] }} ], "minimum_should_match": 1 } }, // 匹配任意病毒相关内容 { "bool": { "should": [ {"match": {"services.no_case_sensitive": "viruses"}}, {"match": {"description.no_case_sensitive": "viruses"}}, {"match": {"services.no_case_sensitive": "virus"}}, {"match": {"description.no_case_sensitive": "virus"}} ], "minimum_should_match": 1 } } ] } } }
3. 验证查询解析结果
使用_validate/query API查看Elasticsearch实际解析的查询逻辑,确认条件是否符合预期:
GET company_descriptions/_validate/query?explain=true { "query": { "query_string": { "query": "(\"Blockchain\" or \"block-chain\" OR \"Blockchain?\" OR \"Block-chain?\" OR \"Distributed Ledger\" OR \"Distributed Ledger?\") AND (\"viruses\" OR \"virus\")", "fields": ["services.no_case_sensitive", "description.no_case_sensitive"] } } }
内容的提问来源于stack exchange,提问作者mattiatantardini

