例子:分组 + 分页 + 条件查询 + 排序操作
- 说明:按照指定字段进行排序,然后对所有记录进行分组,选择每个分组最后一个文档,然后对所有选中的文档进行分页和条件查询等。(此处只是通过聚合操作实现,实际增加一个冗余字段“是否为最新版本”即可)
public Pagination<ServiceImpl> findServiceImplByPage(Integer pageNum, Integer pageSize, ServiceImplQuery implQuery) {
PageRequest pageReq = PageRequest.of(pageNum - 1, pageSize);
SortOperation sortOperation = sort(Sort.by(Sort.Direction.ASC, "versionCode"));
// 构建分组操作
GroupOperation groupOperation = Aggregation.group("serviceImplId");
// 设置需要查询的字段,getPropNameList()方法表示获取类的所有的属性名称,id除外
List<String> propNameList = MongoUtil.getPropNameList(ServiceImplDTO.class);
for (String s : propNameList) {
// 注意:此处需要重新赋值,每次build创建了一个新的对象
groupOperation = groupOperation.last(s).as(s);
}
// 注意:id需要特别处理否则拿不到
groupOperation = groupOperation.last("_id").as("id");
ProjectionOperation projectionOperation = project()
.andExclude("_id")
.and("id").as("_id");
for (String s : propNameList) {
projectionOperation = projectionOperation.and(s).as(s);
}
Aggregation groupAggregation = Aggregation.newAggregation(
sortOperation,
groupOperation,
skip(pageReq.getOffset()),
limit(pageReq.getPageSize()),
projectionOperation);
/* 构建查询条件 */
if (implQuery != null) {
Criteria criteria = new Criteria();
if (CharSequenceUtil.isNotBlank(implQuery.getName())) {
criteria.and("name").regex(".*" + implQuery.getName() + ".*");
}
if (implQuery.getCreateTimeSort() != null) {
groupAggregation.getPipeline().add(sort(Sort.by(
MongoUtil.getSortDirection(implQuery.getCreateTimeSort()), "createTime")));
}
groupAggregation.getPipeline().add(match(criteria));
}
// 执行聚合查询
AggregationResults<ServiceImpl> aggregationResults = mongoTemplate.aggregate(
groupAggregation,
mongoTemplate.getCollectionName(ServiceImpl.class),
ServiceImpl.class
);
// 获取聚合结果
List<ServiceImpl> serviceList = aggregationResults.getMappedResults();
// 计算查询结果的总数
Aggregation countAggregation = Aggregation.newAggregation(groupOperation, count().as("count"));
AggregationResults<CountResult> results = mongoTemplate.aggregate(countAggregation,
mongoTemplate.getCollectionName(ServiceImpl.class), CountResult.class);
CountResult countResult = results.getUniqueMappedResult();
long total = countResult != null ? countResult.getCount() : 0;
return new Pagination<>(serviceList, pageNum, pageSize, total);
}
注意:
- 直接通过聚合操作进行分组只能得到并不能直接得到文档,只能通过 max、min、sum等聚合操作获取相关的值
- 如果想要获取分组内一个文档,需要使用 f i r s t 或 first或 first或last,获取分组内第一个或最后一个文档的字段的值(前提是按照你要的方式进行排序)
- 因为分组查询的特性,_id的值不再是文档的ObjectId,而是被用于分组的字段的值,因此需要特殊处理才能够查出ObjectId
- 通过 first或last(“_id”).as(“identityId”),这样可以会将ObjectId写入identityId字段中,然后使用具有identityId字段的实体类接收即可
- 通过ProjectionOperation排除默认的_id,详情参考代码