如何通过Java MongoDB Driver直接获取指定嵌套文档的rights值
问题
我有如下结构的MongoDB文档集合,希望通过Java MongoDB Driver,根据参数_id、groups._id、roles._id,直接从roles数组中获取对应的rights整数值,而不是先查询整个文档再在Java里提取。
文档结构示例
{ "_id": 1000002, "groups": [ { "_id": 1, "roles": [ { "rights": 3, "_id": 1 }, { "rights": 7, "_id": 2 }, { "rights": 3, "_id": 3 } ] } ], "timestamp": { "$date": { "$numberLong": "1675267318028" } }, "users": [ { "accessProviderId": 1, "rights": 1, "_id": 4 }, { "accessProviderId": 1, "rights": 3, "_id": 5 } ] }
当前实现代码
目前我通过查询整个AccessListItem对象,再手动提取rights值:
Bson fileFilter = Filters.eq("_id", itemId); Bson groupFilter = Filters.elemMatch("groups", Document.parse("{_id:"+groupId+"}")); Bson roleFilter = Filters.elemMatch("groups.roles", Document.parse("{_id:"+role+"}")); Bson finalFilter = Filters.and(fileFilter, Filters.and(groupFilter,roleFilter)); MongoCollection<AccessListItem> accessListItemMongoCollection = MongoUtils.getAccessCollection(type); AccessListItem accessListItem = accessListItemMongoCollection.find(finalFilter).first();
解决方案
可以通过MongoDB的聚合管道或精准投影,直接从数据库获取目标rights值,无需加载整个文档。
方法一:聚合管道精准提取(推荐)
通过多步聚合操作,逐层过滤匹配的数组元素,最终提取rights值,适用于所有场景:
import static com.mongodb.client.model.Aggregates.*; import static com.mongodb.client.model.Filters.*; import static com.mongodb.client.model.Projections.*; // 构建聚合管道 List<Bson> pipeline = Arrays.asList( // 匹配根文档的_id match(eq("_id", itemId)), // 过滤出匹配groupId的groups元素,仅保留该元素 project(fields( include("groups"), computed("groups", filter("groups", eq("$$this._id", groupId))) )), // 展开过滤后的groups数组(此时数组仅含一个元素) unwind("$groups"), // 过滤出匹配roleId的roles元素 project(fields( include("groups.roles"), computed("groups.roles", filter("groups.roles", eq("$$this._id", roleId))) )), // 展开过滤后的roles数组 unwind("$groups.roles"), // 仅保留rights字段 project(include("groups.roles.rights")) ); // 执行聚合查询,返回Document类型结果 MongoCollection<Document> collection = MongoUtils.getAccessCollection(type).withDocumentClass(Document.class); Document result = collection.aggregate(pipeline).first(); // 提取rights整数值 Integer rights = null; if (result != null) { rights = result.getEmbedded(List.of("groups", "roles", "rights"), Integer.class); }
方法二:精准投影提取(适用于唯一匹配场景)
如果能确保每个根文档下仅有一个匹配的group,每个group下仅有一个匹配的role,可以用投影直接定位:
import static com.mongodb.client.model.Filters.*; import static com.mongodb.client.model.Projections.*; // 构建查询过滤器 Bson filter = and( eq("_id", itemId), eq("groups._id", groupId), eq("groups.roles._id", roleId) ); // 构建投影规则,仅保留匹配的rights相关字段 Bson projection = fields( include("groups.roles.rights"), filterArray("groups", eq("_id", groupId)), filterArray("groups.roles", eq("_id", roleId)) ); // 执行查询并投影 Document result = MongoUtils.getAccessCollection(type).withDocumentClass(Document.class) .find(filter) .projection(projection) .first(); // 提取rights值 Integer rights = null; if (result != null) { List<Document> groups = result.getList("groups", Document.class); if (!groups.isEmpty()) { List<Document> roles = groups.get(0).getList("roles", Document.class); if (!roles.isEmpty()) { rights = roles.get(0).getInteger("rights"); } } }
注意事项
- 若存在多个匹配的group或role,方法一会返回所有匹配的rights值,需根据业务逻辑处理;方法二则仅返回第一个匹配项。
- 两种方法都避免了加载整个文档,减少了数据传输量,提升查询效率。
内容的提问来源于stack exchange,提问作者Bojan Stojković
相关产品推荐
相关产品推荐

