如何在SpringBoot中过滤MongoDB中的子文档
解决MongoDB过滤嵌套数组并保留根字段的问题
最优方案:用$filter直接过滤数组(性能优先)
不用展开数组,直接通过$addFields配合$filter筛选符合条件的子文档,同时完整保留根文档的所有字段,效率更高。
Spring Data MongoDB代码示例:
Aggregation aggregation = Aggregation.newAggregation( // 先匹配根文档的login条件 Aggregation.match(Criteria.where("login").is("xxx")), // 过滤subDocs数组,只保留subDocNestedArray包含"AAAA"的子文档 Aggregation.addFields() .addFieldWithValue("subDocs", AggregationExpression.from( "{ $filter: { input: '$subDocs', as: 'doc', cond: { $in: ['AAAA', '$$doc.subDocNestedArray'] } } }" )) .build() ); // 执行聚合查询 List<YourDocumentClass> results = mongoTemplate.aggregate(aggregation, "your-collection-name", YourDocumentClass.class).getMappedResults();
执行后就能得到你期望的结果,根文档的login、someAttribute都会保留,subDocs只留下符合条件的子文档。
修正你原来的unwind+group方案
你之前的代码问题出在group阶段:只按_id分组,没有保留根文档的其他字段,也没正确重组subDocs数组。修正后的代码:
Aggregation aggregation = Aggregation.newAggregation( // 先筛选根文档,减少后续unwind的数据量 Aggregation.match(Criteria.where("login").is("xxx")), // 展开subDocs数组 Aggregation.unwind("$subDocs"), // 筛选包含目标值的子文档 Aggregation.match(Criteria.where("subDocs.subDocNestedArray").is("AAAA")), // 重新分组,保留根字段并重组subDocs数组 Aggregation.group("_id") .first("login").as("login") .first("someAttribute").as("someAttribute") .push("subDocs").as("subDocs") );
这里用$first保留根文档的非数组字段(因为同一_id下这些字段值唯一),用$push把过滤后的子文档重新组装成数组,就能解决字段丢失的问题。
可选方案:用$redact递归过滤
如果一定要用$redact,可以通过递归条件判断保留符合要求的文档部分:
Aggregation aggregation = Aggregation.newAggregation( Aggregation.match(Criteria.where("login").is("xxx")), Aggregation.redact(AggregationExpression.from( "{ $cond: { if: { $or: [ { $eq: ['$login', 'xxx'] }, { $in: ['AAAA', '$subDocNestedArray'] } ] }, then: '$$DESCEND', else: '$$PRUNE' } }" )) );
$redact会递归遍历文档结构:满足条件时用$$DESCEND继续检查子文档,不满足时用$$PRUNE移除当前文档/字段。注意要确保条件覆盖根文档和子文档的判断,避免误删有效字段。
内容的提问来源于stack exchange,提问作者pibou
相关产品推荐
相关产品推荐

