Spring Data MongoDB中$elemMatch结合$in的查询及转换问题
解决方案:Mongo原生查询与Spring Data转换
一、正确的Mongo原生查询(无需聚合)
你需要明确**查询条件(筛选符合要求的文档)和投影(控制返回的字段/数组元素)**的不同作用:
- 你的第一个查询中,
$elemMatch是作为查询条件,仅用于筛选出包含至少一个符合条件数组元素的文档,但返回的是完整文档(所有journeyDates元素),并非过滤数组内容。 - 第二个查询用
$elemMatch做投影,这是Mongo的默认特性:只会返回数组中第一个匹配的元素。
要同时实现「筛选目标文档」和「返回数组中所有匹配元素」,使用$filter做投影即可:
db.journeys.find( // 查询条件:匹配顶级_id._id,且journeyDates中存在period在[1,2]的元素 { "_id._id": "0104", "journeyDates.period": { $in: [1, 2] } }, // 投影:保留_id,并用$filter返回所有符合条件的journeyDates元素 { "_id": 1, journeyDates: { $filter: { input: "$journeyDates", as: "date", cond: { $in: ["$$date.period", [1, 2]] } } } // 若需要保留其他顶级字段,添加类似 "otherField": 1 的配置 } )
注:查询条件里的"journeyDates.period": { $in: [1,2] }和$elemMatch效果一致,单个条件下用点语法更简洁;如果数组元素需要匹配多个条件,才需要用$elemMatch(比如journeyDates: { $elemMatch: { period: { $in: [1,2] }, status: "active" } })。
二、转换为Spring Data Query对象
方式1:使用Spring Data API构造Query
假设你的实体类结构如下:
public class Journey { private Id _id; private List<JourneyDate> journeyDates; // 其他字段、getter/setter public static class Id { private String _id; // getter/setter } public static class JourneyDate { private Integer period; // 其他字段、getter/setter } }
通过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 static org.springframework.data.mongodb.core.aggregation.Aggregation.filter; // 注入MongoTemplate @Autowired private MongoTemplate mongoTemplate; public List<Journey> getMatchingJourneys() { // 1. 构造查询条件 Criteria criteria = Criteria.where("_id._id").is("0104") .and("journeyDates.period").in(1, 2); // 2. 构造投影:用$filter保留所有匹配的journeyDates元素 Query query = Query.query(criteria) .projection() .and("_id").include() // 保留_id字段 .and("journeyDates") .apply(filter("journeyDates") .as("date") .by(Criteria.where("$$date.period").in(1, 2))); // 3. 执行查询 return mongoTemplate.find(query, Journey.class); }
方式2:使用原生JSON构造BasicQuery
如果更习惯原生Mongo语法,也可以直接用JSON字符串构造查询:
import org.springframework.data.mongodb.core.query.BasicQuery; public List<Journey> getMatchingJourneysWithBasicQuery() { String queryJson = "{ \"_id._id\": \"0104\", \"journeyDates.period\": { $in: [1, 2] } }"; String projectionJson = "{ \"_id\": 1, \"journeyDates\": { $filter: { input: \"$journeyDates\", as: \"date\", cond: { $in: [\"$$date.period\", [1, 2]] } } } }"; BasicQuery basicQuery = new BasicQuery(queryJson, projectionJson); return mongoTemplate.find(basicQuery, Journey.class); }
内容的提问来源于stack exchange,提问作者vkourt
相关产品推荐
相关产品推荐

