Spring Data MongoDB中如何向@Query注解传递数组?
Spring Boot MongoDB 自定义$or查询报错解决
问题场景
需要在Spring Boot中结合MongoDB编写自定义查询,向$or操作符传递数组条件。
MongoDB目标查询语句:
{ "title": {"$regex":"Ab","$options":"i"}, "$or": [{"genre": "Romance"}, {"genre": "Music"}], "release_date": {"$gte": "2000", "$lte": "2020"} }
编写的Spring Boot Repository代码:
@Query("{title: { '$regex':?0,'$options':'i' }, $or: ?1, release_date: { $gte: ?2, $lte: ?3 }}") List<Movie> findAllByFilters(String title, String[] genres, String min_date, String max_date);
执行后抛出错误:
Query failed with error code 2 with name 'BadValue' and error message '$or/$and/$nor entries need to be full objects'
错误原因
MongoDB的$or操作符要求每个元素都是完整的查询对象(比如{genre: 'Romance'}),但直接传递字符串数组["Romance", "Music"]时,Spring Data MongoDB会把它原样传入查询,导致$or的元素是字符串而非对象,触发解析错误。
解决方案
方案1:使用SpEL表达式在@Query中转换数组
直接在@Query注解里通过SpEL表达式,将传入的字符串数组转换为$or需要的对象数组:
@Query("{title: { '$regex':?0,'$options':'i' }, " + "$or: ?1.![{genre: #this}], " + "release_date: { $gte: ?2, $lte: ?3 }}") List<Movie> findAllByFilters(String title, String[] genres, String min_date, String max_date);
这里?1.![{genre: #this}]的作用是:遍历参数?1(即genres数组),将每个元素#this包装为{genre: 元素值}的对象,最终生成符合$or要求的对象数组。
方案2:手动构造$or条件对象列表
在Service层将字符串数组转换为包含查询条件的对象列表,再传入Repository:
- 修改Repository方法的参数类型:
@Query("{title: { '$regex':?0,'$options':'i' }, $or: ?1, release_date: { $gte: ?2, $lte: ?3 }}") List<Movie> findAllByFilters(String title, List<Document> orConditions, String min_date, String max_date);
- Service层处理参数:
import org.bson.Document; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; // 调用示例 String[] genres = {"Romance", "Music"}; List<Document> orConditions = Arrays.stream(genres) .map(genre -> new Document("genre", genre)) .collect(Collectors.toList()); List<Movie> movies = movieRepository.findAllByFilters("Ab", orConditions, "2000", "2020");
方案3:使用MongoTemplate动态构建查询
如果需要更灵活的查询逻辑,推荐使用MongoTemplate直接构建查询:
import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; // 注入MongoTemplate private final MongoTemplate mongoTemplate; public List<Movie> findMoviesByFilters(String title, String[] genres, String minDate, String maxDate) { Query query = new Query(); // 标题模糊匹配 query.addCriteria(Criteria.where("title").regex(title, "i")); // 构造$or条件 List<Criteria> orCriterias = Arrays.stream(genres) .map(genre -> Criteria.where("genre").is(genre)) .collect(Collectors.toList()); query.addCriteria(new Criteria().orOperator(orCriterias.toArray(new Criteria[0]))); // 日期范围过滤 query.addCriteria(Criteria.where("release_date").gte(minDate).lte(maxDate)); return mongoTemplate.find(query, Movie.class); }
内容的提问来源于stack exchange,提问作者Tomas Caceres
相关产品推荐
相关产品推荐

