Elasticsearch 6.8.14自定义状态权重排序实现(Spring Data Java)
实现方案与代码示例
核心实现思路
针对你的自定义排序需求,有两种可行方案:
- 新增权重字段(推荐):给车辆实体添加
statusWeight字段,将每个status映射为对应的优先级数值(比如BOUGHT=1,IN PRODUCTION=2,IN TESTS=3,APPROVED=4),排序时直接按该字段升序排列。这种方式性能最优,因为是预计算的字段排序,适合数据量较大的场景。 - 脚本排序(无字段侵入):使用Elasticsearch的
script_sort,在查询时动态根据status计算排序权重。这种方式无需修改实体结构,但性能略差,适合小数据集或优先级频繁变动的场景。
方案一:新增权重字段实现
1. 修改车辆实体类
在实体类中添加statusWeight字段,并在设置status时自动赋值对应的权重:
import org.springframework.data.annotation.Id; import org.springframework.data.elasticsearch.annotations.Document; import org.springframework.data.elasticsearch.annotations.Field; import org.springframework.data.elasticsearch.annotations.FieldType; @Document(indexName = "vehicles", type = "_doc") public class Vehicle { @Id private String id; @Field(type = FieldType.Keyword) private String mark; // 品牌 @Field(type = FieldType.Keyword) private String colour; // 颜色 @Field(type = FieldType.Keyword) private String status; // 状态:BOUGHT、IN PRODUCTION、IN TESTS、APPROVED @Field(type = FieldType.Integer) private Integer statusWeight; // 状态权重 // 构造器、getter、setter public void setStatus(String status) { this.status = status; // 根据status设置对应的权重 switch(status) { case "BOUGHT": this.statusWeight = 1; break; case "IN PRODUCTION": this.statusWeight = 2; break; case "IN TESTS": this.statusWeight = 3; break; case "APPROVED": this.statusWeight = 4; break; default: this.statusWeight = 5; // 未知状态权重设为最低 } } }
2. 编写Repository查询方法
使用Spring Data Elasticsearch的Sort构造排序规则,先按mark升序,再按colour升序,最后按statusWeight升序:
import org.springframework.data.domain.Sort; import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; import java.util.List; public interface VehicleRepository extends ElasticsearchRepository<Vehicle, String> { // 自定义排序查询所有车辆 List<Vehicle> findAll(Sort sort); // 或者按条件查询+排序,比如按品牌查询 List<Vehicle> findByMark(String mark, Sort sort); }
3. 调用查询接口
在业务代码中构造排序规则并调用:
import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.List; @Service public class VehicleService { @Resource private VehicleRepository vehicleRepository; public List<Vehicle> getAllVehiclesSorted() { // 构造排序规则:mark升序 -> colour升序 -> statusWeight升序 Sort sort = Sort.by( Sort.Order.asc("mark"), Sort.Order.asc("colour"), Sort.Order.asc("statusWeight") ); return vehicleRepository.findAll(sort); } public List<Vehicle> getVehiclesByMarkSorted(String mark) { Sort sort = Sort.by( Sort.Order.asc("colour"), Sort.Order.asc("statusWeight") ); return vehicleRepository.findByMark(mark, sort); } }
方案二:脚本排序实现(无需新增字段)
如果不想修改实体结构,可以使用Elasticsearch的脚本排序,在查询时动态计算status的优先级:
import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.sort.SortBuilders; import org.elasticsearch.search.sort.SortOrder; import org.springframework.data.elasticsearch.core.ElasticsearchTemplate; import org.springframework.data.elasticsearch.core.query.NativeSearchQuery; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.List; @Service public class VehicleService { @Resource private ElasticsearchTemplate elasticsearchTemplate; public List<Vehicle> getAllVehiclesWithScriptSort() { // 编写Painless脚本,根据status返回对应的权重值 String script = "if (doc['status'].value == 'BOUGHT') return 1; " + "else if (doc['status'].value == 'IN PRODUCTION') return 2; " + "else if (doc['status'].value == 'IN TESTS') return 3; " + "else if (doc['status'].value == 'APPROVED') return 4; " + "return 5;"; // 构造原生查询,设置排序规则 NativeSearchQuery query = new NativeSearchQuery(QueryBuilders.matchAllQuery()) .addSort(SortBuilders.fieldSort("mark").order(SortOrder.ASC)) .addSort(SortBuilders.fieldSort("colour").order(SortOrder.ASC)) .addSort(SortBuilders.scriptSort(script, "number").order(SortOrder.ASC)); return elasticsearchTemplate.queryForList(query, Vehicle.class); } }
是否需要新增权重字段?
推荐新增权重字段,原因如下:
- 性能更优:字段排序是Elasticsearch最擅长的操作,比脚本排序的执行效率高很多,尤其是在数据量较大时差距明显。
- 维护更方便:如果后续status的优先级需要调整,只需要修改实体类中设置权重的逻辑,重新同步数据即可,无需修改所有查询语句。
- 兼容性更好:Spring Data Elasticsearch对字段排序的支持更成熟,避免了脚本语法版本兼容问题(比如Elasticsearch不同版本的Painless脚本可能有细微差异)。
内容的提问来源于stack exchange,提问作者Frankio1
相关产品推荐
相关产品推荐

