如何使用QueryBuilder在Elasticsearch中实现浮点字段文本搜索
浮点字段文本检索实现方案
问题根因
你遇到的问题由两个核心原因导致:
- 直接给
Float类型的实体字段标注FieldType.Text不会生效:Spring Data Elasticsearch序列化Float字段时,传入ES的是数值类型值,ES不会自动将数值转成文本做分词、构建倒排索引,哪怕映射标注为Text,实际存储逻辑也不支持文本匹配。 - 字段设为
FieldType.Float时,ES存储的是原生数值类型,仅支持精确数值匹配、范围查询、排序、聚合操作,完全不支持*xxx*这类依赖倒排索引的通配符文本检索。
另外你现有分页逻辑有bug:总条数传的是当前页结果集的size(),会导致分页组件永远识别不到总数据量,分页功能异常,后面的代码会一并修复。
最优实现方案:多字段映射
用ES的多字段(Multi-fields)能力,同一个currentPrice字段同时存两份数据:主字段存浮点型,支撑价格筛选、排序、聚合等数值类操作;新增一个内部文本子字段,自动把浮点值转成字符串建倒排索引,支撑模糊文本检索,不需要改动原有业务逻辑。
第一步:修改实体类字段注解
将原来currentPrice的单@Field配置替换为多字段配置:
import org.springframework.data.elasticsearch.annotations.*; // 其他类上注解、其余字段配置保持不变 @Column(name = "current_price") @MultiField( mainField = @Field(type = FieldType.Float), // 主字段:存浮点型,供数值类操作使用 otherFields = { @InnerField( suffix = "text", type = FieldType.Text, analyzer = "standard" ) // 内部子字段:存文本格式的价格,供模糊检索使用 } ) private Float currentPrice;
第二步:调整搜索方法
显式指定检索字段列表,把价格的文本子字段加入检索范围,同时修复分页总条数的bug:
public Page<Plan> search(String query, Pageable pageable) { NativeSearchQuery nativeSearchQuery = new NativeSearchQuery( queryStringQuery("*" + query + "*") .defaultOperator(Operator.AND) // 显式声明所有需要参与模糊检索的字段,包含价格的文本子字段 .fields("name", "description", "currentPrice.text") ); nativeSearchQuery.setPageable(pageable); SearchHits<Plan> searchHits = elasticsearchTemplate.search(nativeSearchQuery, Plan.class); List<Plan> hits = searchHits .map(SearchHit::getContent) .stream() .collect(Collectors.toList()); // 修复分页bug:总条数取ES返回的实际命中总数,不是当前页结果数量 return new PageImpl<>(hits, pageable, searchHits.getTotalHits()); }
第三步:重建索引
配置修改完成后,必须删除ES中已存在的plan索引,重启服务让ES根据新的实体注解重新生成索引映射,再重新导入全量业务数据,否则旧索引的映射规则不会更新,配置无法生效。
注意事项
- 不建议直接把
currentPrice字段的实体类型改成String:会直接丢失数值类型的查询、排序、聚合能力,后续做价格区间筛选、排序等需求时实现成本极高。 - 前缀带
*的通配符查询性能较差,如果单索引数据量超过百万级,建议替换为nGram分词器实现子串匹配,查询性能会有数量级提升。
内容的提问来源于stack exchange,提问作者barsawi13
相关产品推荐
相关产品推荐

