You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 01:55:37