Elasticsearch与Spring Data中Invoice类型rate字段存在性查询报错解决
解决Spring Data Elasticsearch中对象类型字段的Exists/IsNull查询问题
这问题我之前踩过坑——Spring Data Elasticsearch的方法命名查询对对象类型字段的存在性判断,确实没法直接用findByXxxExists或者findByXxxIsNull,因为它的方法名解析器对复杂类型的处理逻辑和基本类型不一样,就会抛出你看到的Illegal criteria found错误。
下面给你两种亲测有效的解决方案:
方案一:用@Query注解手写ES DSL查询
这是最直接快速的方式,直接在仓库方法上标注@Query,传入Elasticsearch原生的exists查询DSL:
public interface MyRepo extends ElasticsearchRepository<Invoice, String> { // 查询rate字段存在的文档 @Query("{\"bool\": {\"must\": [{\"exists\": {\"field\": \"rate\"}}]}}") List<Invoice> findByRateExists(); // 查询rate字段不存在的文档(对应原需求的IsNull场景) @Query("{\"bool\": {\"must_not\": [{\"exists\": {\"field\": \"rate\"}}]}}") List<Invoice> findByRateIsNull(); }
原理很简单:ES的exists查询会判断指定字段是否有非null值(对于对象类型来说,就是该对象是否被实例化并存入ES);must_not exists则刚好相反,匹配字段不存在或为null的文档。
方案二:用Criteria API构建查询(更灵活)
如果后续需要扩展更复杂的查询逻辑,推荐用自定义仓库+Criteria API的方式,可读性和扩展性更好:
- 先定义主仓库和自定义接口:
// 主仓库接口,继承自定义接口 public interface MyRepo extends ElasticsearchRepository<Invoice, String>, MyRepoCustom { } // 自定义方法接口 public interface MyRepoCustom { List<Invoice> findByRateExists(); List<Invoice> findByRateIsNull(); }
- 实现自定义接口:
import org.springframework.data.elasticsearch.core.ElasticsearchOperations; import org.springframework.data.elasticsearch.core.query.Criteria; import org.springframework.data.elasticsearch.core.query.CriteriaQuery; import org.springframework.data.elasticsearch.core.query.Query; public class MyRepoImpl implements MyRepoCustom { private final ElasticsearchOperations elasticsearchOperations; // 注入ElasticsearchOperations public MyRepoImpl(ElasticsearchOperations elasticsearchOperations) { this.elasticsearchOperations = elasticsearchOperations; } @Override public List<Invoice> findByRateExists() { // 构建rate字段存在的查询条件 Query query = new CriteriaQuery(Criteria.where("rate").exists()); return elasticsearchOperations.queryForList(query, Invoice.class); } @Override public List<Invoice> findByRateIsNull() { // 构建rate字段不存在的查询条件 Query query = new CriteriaQuery(Criteria.where("rate").not().exists()); return elasticsearchOperations.queryForList(query, Invoice.class); } }
这种方式的优势是可以灵活组合多个查询条件,比如后续要加其他过滤条件,直接在Criteria里追加即可。
额外说明
如果你的rate对象包含子字段(比如rate.amount),需要判断子字段存在性的话,只需要把字段路径改成对应的全路径(比如rate.amount)就行,两种方案都适用。
内容的提问来源于stack exchange,提问作者Héctor Valls
相关产品推荐
相关产品推荐

