如何用Searchkick和Elasticsearch为同一字段添加多范围筛选
在Rails应用中,Product模型通过Searchkick与Elasticsearch集成,esp字段的单范围筛选可通过以下方式实现:
Product.search('*', where: { esp: 100..200 })
但直接尝试多范围筛选时会触发错误,以下是两种错误写法及对应报错:
错误写法1
Product.search('*', where: { esp: [100..200, 500..600] })
报错信息:
Searchkick::InvalidQueryError ([400] {"error":{"root_cause":[{"type":"query_shard_exception","reason":"failed to create query: multiple points","index_uuid":"bMBebzhDSB2QKnaynoOHWQ","index":"public_products_development_20240202154554635"}],"type":"search_phase_execution_exception","reason":"all shards failed","phase":"query","grouped":true,"failed_shards":[{"shard":0,"index":"public_products_development_20240202154554635","node":"8-khPyjBS5SmR3Pea20QRg","reason":{"type":"query_shard_exception","reason":"failed to create query: multiple points","index_uuid":"bMBebzhDSB2QKnaynoOHWQ","index":"public_products_development_20240202154554635","caused_by":{"type":"number_format_exception","reason":"multiple points"}}]},"status":400})
错误写法2
Product.search('*', where: { esp: [{gte: 0, lte: 100}, {gte: 500, lte: 1000}] })
报错信息:
Searchkick::InvalidQueryError ([400] {"error":{"root_cause":[{"type":"query_shard_exception","reason":"failed to create query: For input string: "{"","index_uuid":"bMBebzhDSB2QKnaynoOHWQ","index":"public_products_development_20240202154554635"}],"type":"search_phase_execution_exception","reason":"all shards failed","phase":"query","grouped":true,"failed_shards":[{"shard":0,"index":"public_products_development_20240202154554635","node":"8-khPyjBS5SmR3Pea20QRg","reason":{"type":"query_shard_exception","reason":"failed to create query: For input string: "{"","index_uuid":"bMBebzhDSB2QKnaynoOHWQ","index":"public_products_development_20240202154554635","caused_by":{"type":"number_format_exception","reason":"For input string: "{""}}]},"status":400})
正确解决方案
Searchkick支持通过布尔逻辑组合多个范围条件,有两种实现方式:
方式1:使用查询块(Query Block)
利用Searchkick的DSL直接构造或逻辑连接的范围筛选:
Product.search('*') do where { (esp >= 100) & (esp <= 200) | (esp >= 500) & (esp <= 600) } end
方式2:直接构造Elasticsearch布尔查询
通过where参数传入符合Elasticsearch语法的布尔查询结构:
Product.search('*', where: { bool: { should: [ { range: { esp: { gte: 100, lte: 200 } } }, { range: { esp: { gte: 500, lte: 600 } } } ], minimum_should_match: 1 } })
这两种方式都会生成Elasticsearch的多范围筛选查询,满足esp字段匹配任意一个指定范围的需求。
内容的提问来源于stack exchange,提问作者Parth Thakur

