前因
最近项目上又新需求了,相关的统计信息接口,需要在MongoDB中实现类似mysql多表关联查询,那么问题来了,MongoDB要如何才能像Hibernate那样一对一,一对多映射关系了?本节讲借助loopup和unwind组合方式来实现此功能
需求
-
以勋章任务为主表查询勋章任务名称及相关配置信息
-
导出每个人任务下对应的文件结果对比信息
备注:涉及文档 Medal(勋章)、MedalTask(勋章任务)、MedalTaskFile(任务明细),文档从左到右一对多关系
实现
之前版本
实现思路
-
分页查询MedalTask,取出MedalId集合和taskIds集合
-
分别从Medal和MedalTaskFile查询数据集合,进行二次聚合
Aggregation进阶之lookup
先贴代码
/*数据聚合*/
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(criteria),
/*关联勋章*/
Aggregation.lookup("medal", "medalId", "_id", "medal"),
/*关联任务*/
Aggregation.lookup("medalTaskFile", "_id", "medalTaskId", "taskFiles"),
/*查询起始值*/
Aggregation.skip(params.getPageNum() > 1 ? (params.getPageNum() - 1) * params.getPageSize() : 0),
/*分页大小*/
Aggregation.limit(params.getPageSize()),
/*排序*/
Aggregation.sort(Sort.by(Sort.Order.desc("createdTime"))),
/*打散Medal*/
Aggregation.unwind("medal")
);
List<MedalUserTo> medalUserTos = medalV4MongoTemplate.aggregate(aggregation, MedalTask.class, MedalUserTo.class).getMappedResults();
@Data
private class MedalUserTo {
private String medalId;
private String userId;
private Integer status;
private Double rate;
private Instant createdTime;
private Instant submitTime;
private Medal medal;
private List<MedalTaskFile> taskFiles;
}
方法解读
方法 | 参数 | 备注 |
---|---|---|
match() | (Criteria criteria) | 查询条件构建 |
lookup() | (String from, String localField, String foreignField, String as) | from:关联表localField:主记录关联字段,传入的是MongoDB中的字段名,非实体类字段名foreignField:关联表关联字段,字段名同上as:别名,及实体类映射字段名(lookup默认返回的类型是ArrayList,相关的字段接收默认需要使用集合接收,类似一对多这种映射关系) |
skip() | (int elementsToSkip)(long elementsToSkip) | 查询起始值 |
limit() | (long maxElements) | 最大element数量,及分页大小 |
sort() | (Sort sort) | 排序字段 |
unwind() | (String field) | 展开这个字段主要是用于聚合记录拆分,把对应的集合字段(长度为n)对应的主记录拆分n个对象,字段名对应是集中中单个文档对象本段代码使用场景,在已知一对一场景下,把lookup关联的对象由数组编程单对象(注:若不是一对一关系,则可能出现重复对象),所以在MedalUserTo中可以使用Medal对象接收该参数 |
project() | (String... fields) | 控制显示查询字段,可用于返回指定字段 |
unwind和project在MongoDB组合查询示例
正常查询
//正常记录查询,这里屏蔽相关字段,主要看数据结构
db.operationLog.aggregate([
{$match:{_id:'DBDF7FA8C72C4606A15CF250FF35A530'}},
{$project:{_id:1,batchIdList:1}}
])
结果
{
"_id" : "DBDF7FA8C72C4606A15CF250FF35A530",
"batchIdList" : [
{
"taskId" : "721EB7250C0049B49A5AB4A734498F2B",
"batchInfos" : {}
},
{
"taskId" : "E1C60CAD114D405B916E66A05256C07B",
"batchInfos" : {}
}
]
}
使用unwind拆分查询记录
db.operationLog.aggregate([
{$match:{_id:'DBDF7FA8C72C4606A15CF250FF35A530'}},
{$unwind:"$batchIdList"}
])
结果
{
"_id" : "DBDF7FA8C72C4606A15CF250FF35A530",
"batchIdList" : {
"taskId" : "721EB7250C0049B49A5AB4A734498F2B",
"batchInfos" : {}
}
}
{
"_id" : "DBDF7FA8C72C4606A15CF250FF35A530",
"batchIdList" : {
"taskId" : "E1C60CAD114D405B916E66A05256C07B",
"batchInfos" : {}
}
}
对Aggregation的group、lookup、project和unwind方法进行灵活组合,大大精简了我们对MongoDB的关联查询、统计查询及相关聚合查询等操作代码编写