如何在Elasticsearch/OpenSearch中利用脚本字段计算值实现过滤?
解决Elasticsearch/OpenSearch中动态计算字段的过滤问题(基于Searchkick)
核心问题原因
script_fields是在查询结果返回阶段计算的,无法在过滤/查询阶段被引用,所以直接用它做range过滤行不通。
解决方案1:使用Script Filter直接计算过滤
不需要提前索引计算值,直接在bool的filter里用脚本判断计算结果是否符合范围。
Elasticsearch/OpenSearch 查询示例
{ "_source": ["*"], "query": { "function_score": { "query": { "bool": { "filter": { "script": { "source": "params.total_stay * doc['default_rate'].value >= params.min_fee && params.total_stay * doc['default_rate'].value <= params.max_fee", "params": { "total_stay": 12, "min_fee": 12, "max_fee": 100 } } } } }, "boost_mode": "replace", "score_mode": "first" } }, "script_fields": { "total_stay_fee": { "script": { "source": "params.total_stay * doc['default_rate'].value", "params": { "total_stay": 12 } } } } }
Searchkick 中的实现(Ruby on Rails)
Model.search( "*", body: { query: { function_score: { query: { bool: { filter: { script: { source: "params.total_stay * doc['default_rate'].value >= params.min_fee && params.total_stay * doc['default_rate'].value <= params.max_fee", params: { total_stay: 12, min_fee: 12, max_fee: 100 } } } } }, boost_mode: "replace", score_mode: "first" } }, script_fields: { total_stay_fee: { script: { source: "params.total_stay * doc['default_rate'].value", params: { total_stay: 12 } } } } } )
解决方案2:使用Runtime Fields(推荐)
Runtime Fields 是在查询阶段动态计算的虚拟字段,可像普通字段一样用于过滤、排序和聚合,且无需修改现有索引结构。
Elasticsearch/OpenSearch 查询示例
{ "_source": ["*"], "runtime_mappings": { "total_stay_fee": { "type": "double", "script": { "source": "emit(params.total_stay * doc['default_rate'].value)", "params": { "total_stay": 12 } } } }, "query": { "function_score": { "query": { "bool": { "filter": { "range": { "total_stay_fee": { "gte": 12, "lte": 100 } } } } }, "boost_mode": "replace", "score_mode": "first" } } }
Searchkick 中的实现
Model.search( "*", body: { runtime_mappings: { total_stay_fee: { type: "double", script: { source: "emit(params.total_stay * doc['default_rate'].value)", params: { total_stay: 12 } } } }, query: { function_score: { query: { bool: { filter: { range: { total_stay_fee: { gte: 12, lte: 100 } } } } }, boost_mode: "replace", score_mode: "first" } } } )
兼容性说明
以上两种方案均支持Elasticsearch 7.x+ 和 OpenSearch 1.x+,可通过对应API直接实现。
内容的提问来源于stack exchange,提问作者Code father
相关产品推荐
相关产品推荐

