ElasticSearch如何仅检索指定对象(如meta)下的字符串类型字段
在ElasticSearch中使用通配符指定meta.*作为检索字段范围时,查询会覆盖meta对象下所有类型的字段,而文本类查询仅能作用于字符串类型字段,非字符串字段会干扰查询逻辑,需要实现仅筛选指定对象下的字符串类型字段参与检索。
当前使用的查询写法如下,该写法会纳入meta下所有类型字段:
GET /people/_search { "query": { "bool": { "must": { "query_string": { "query": "severe*", "auto_generate_synonyms_phrase_query": false, "fields": ["meta.*"] } } } }, "size": 10 }
现有返回样例中,meta.corrosion是布尔类型字段,这类非字符串字段不应参与文本匹配:
{ "_index" : "people", "_type" : "_doc", "_id" : "fd0eebef-62a8-4c46-a199-685327655", "_score" : 1.0, "_source" : { "id" : "fd0eebef-62a8-4c46-a199-35267523556", "createdAt" : "2022-04-26T07:23:30.899Z", "lastUpdate" : "2022-04-26T07:27:28.123Z", "meta" : { "status" : "finished", "corrosion" : true, "detectionStatus" : "severe" }, "owner" : "john.doe", "perms" : { "*" : { "allowed_users" : [ "OWNER" ] } } } }
方案1:查询时按类型过滤字段(无需修改索引,适配动态字段场景)
query_string查询的fields参数支持在路径通配后追加字段类型过滤,语法为[字段路径通配]:[字段类型],可以直接筛选指定路径下对应类型的字段参与检索,无需提前枚举所有字段名。
由于ElasticSearch 5.x之后已将原有的string类型拆分为text(分词全文检索)和keyword(不分词精确匹配)两类字符串类型,只需将原查询中的fields配置从["meta.*"]替换为["meta.*:text", "meta.*:keyword"],即可自动跳过meta下的布尔、数值、日期等非字符串字段。
修改后的查询语句:
GET /people/_search { "query": { "bool": { "must": { "query_string": { "query": "severe*", "auto_generate_synonyms_phrase_query": false, "fields": ["meta.*:text", "meta.*:keyword"] } } } }, "size": 10 }
这种写法适配性最好,meta下后续新增的字符串字段会自动被纳入检索范围,不需要修改查询语句,支持7.11及以上版本的ElasticSearch。
方案2:索引层面聚合字符串字段(性能更优,适合固定业务场景)
如果业务场景中meta下的字段相对固定,或者对查询性能要求更高,可以通过动态模板+copy_to的方式,将meta下所有字符串字段的值聚合到一个专门的全文检索字段中,查询时直接检索该字段即可,从根本上避免非字符串字段干扰。
索引创建时添加如下配置:
PUT /people { "mappings": { "properties": { "meta": { "dynamic_templates": [ { "meta_string_fields": { "path_match": "meta.*", "match_mapping_type": "string", "mapping": { "type": "text", "copy_to": "meta_all_text" } } } ] }, "meta_all_text": { "type": "text" } } } }
配置生效后,meta路径下所有新增的字符串类型字段,都会自动将值拷贝到meta_all_text字段中,查询时直接将fields设置为["meta_all_text"]即可,不需要做通配匹配,查询性能远高于多字段通配检索。注意该方案需要在索引创建阶段配置,存量历史数据需要重建索引才能生效。
- 低版本(7.11以下)ElasticSearch不支持查询时按类型过滤字段的语法,这类版本要么手动枚举meta下所有字符串字段名写入fields列表,要么采用方案2的索引配置方式实现需求
- 字段类型过滤不要写
meta.*:string,该类型在高版本ES中已不存在,必须同时指定text和keyword才能覆盖所有字符串字段 - 示例中用到的前缀通配查询(
severe*)在text和keyword类型字段上都可以正常执行,不需要额外修改查询逻辑
内容的提问来源于stack exchange,提问作者Gael Musi

