如何用MongoTemplate基于关联集合的operationId过滤Report数据?
解决MongoTemplate通过operationId过滤Report的问题
问题分析
你的两种方法都存在关键问题,导致查询无结果:
- 直接Criteria查询:MongoDB的DBRef仅存储引用关系,不支持直接跨集合查询嵌套的关联字段,因此
file.operation.$id这种写法无法触发跨集合关联,自然查不到数据。 - Lookup聚合查询:你的聚合操作遗漏了
MatchOperation,导致过滤条件根本没执行;同时还要注意字段类型匹配(日期、ObjectId/字符串)是否正确。
正确实现方案
方案一:使用聚合Lookup关联查询
修正聚合步骤,确保lookup、unwind、match顺序正确,同时注意字段类型匹配:
// 1. 构建过滤条件 List<Criteria> criteriaList = new ArrayList<>(); // 注意:如果generated_date存储的是Date/LocalDate类型,用LocalDate对象匹配,而非字符串 criteriaList.add(Criteria.where("generated_date").is(LocalDate.parse("2023-05-12"))); // 关联_file后,过滤Operation的_id(注意:如果Operation的id是字符串,无需转ObjectId) criteriaList.add(Criteria.where("_file.operation.$id").is(createObjectIdFromString(client.getId()))); Criteria operationCriteria = new Criteria().andOperator(criteriaList); // 2. 构建聚合操作 LookupOperation lookupOperation = Aggregation.lookup( "file", // 要关联的File集合名 "file.$id", // Report集合中关联File的DBRef的$id字段 "_id", // File集合中的主键字段 "_file" // 关联后输出的字段名 ); UnwindOperation unwindOperation = Aggregation.unwind("$_file"); // 展开lookup返回的数组 MatchOperation matchOperation = Aggregation.match(operationCriteria); // 3. 按顺序执行聚合:先关联,再展开,最后过滤 Aggregation aggregation = Aggregation.newAggregation(lookupOperation, unwindOperation, matchOperation); // 4. 执行查询 AggregationResults<Report> results = mongoTemplate.aggregate(aggregation, "report", Report.class); List<Report> reports = results.getMappedResults();
方案二:分步查询(更简单)
先查询关联目标operationId的所有File的id,再根据这些id查询对应的Report:
// 第一步:查询所有关联指定operationId的File的id List<ObjectId> fileIds = mongoTemplate.find( Criteria.where("operation.$id").is(createObjectIdFromString(client.getId())), File.class ).stream() .map(File::getId) .map(ObjectId::new) // 如果File的id是字符串,去掉这步,直接用String类型 .collect(Collectors.toList()); // 第二步:查询符合条件的Report List<Report> reports = mongoTemplate.find( new Criteria().andOperator( Criteria.where("generated_date").is(LocalDate.parse("2023-05-12")), Criteria.where("file.$id").in(fileIds) ), Report.class );
关键注意点
- 字段类型匹配:
- 如果Operation的
id是字符串而非ObjectId,那么过滤时直接用client.getId()字符串匹配,不要转成ObjectId。 generated_date如果存储的是Date类型,必须用LocalDate/Date对象匹配,不能用字符串"2023-05-12"。
- 如果Operation的
- 聚合顺序:Lookup返回的是数组,必须先unwind再执行match过滤,否则无法正确匹配嵌套字段。
内容的提问来源于stack exchange,提问作者MiMoy
相关产品推荐
相关产品推荐

