2 MongoDB 脚本
$project
: 定义字段,不定义,在后续的 $group 中不能用。用 userId 字段代表 userId 字段,用 $dateToString 处理 createTime 字段,代表 month。
第一次$group
: 指定 month + userId 共同作为聚合条件,并 sum(1) 汇总每个条件对应的条数。
第二次$group
: 指定第一次聚合条件中的 month 为新的聚合条件,并 sum 之前统计好的条数,得到总的条数,并再次 sum(1) 条数(等同于 count),得到有多少个不同的 userId。
db.testDoc.aggregate([
$match: {
createTime: {
$gte: ISODate("2021-01-01T00:00:00.000+08:00"),
$lte: ISODate("2021-12-31T23:59:59.000+08:00")
$project: {
userId: "$userId",
month: {
$dateToString: {
format: "%Y-%m",
date: "$createTime"
$group: {
_id: {
"month": "$month",
"userId": "$userId"
count: {
$sum: 1
$group: {
_id: {
"month": "$_id.month"
totalCount: {
$sum: "$count"
userCount: {
$sum: 1
$sort: {
_id: 1
定义一个 Aggregation,开始往里拼各个子语句。后面定义一个 CountDto 对象接收这个有三个字段的统计结果。
Aggregation aggregate = Aggregation.newAggregation(
Aggregation.match(CriteriaBuilder.between("createTime", startTime, endTime)),
Aggregation.project("userId")
.and(DateOperators.DateToString.dateOf("createTime").toString("%Y-%m")).as("month"),
Aggregation.group("month", "userId").count().as("muCount"),
Aggregation.group("month").sum("muCount").as("totalCount").count().as("userCount"),
Aggregation.sort(Direction.ASC, "_id"),
Aggregation.project().andExpression("_id").as("month")
.andExpression("totalCount").as("totalCount")
.andExpression("userCount").as("userCount")
List<CountDto> resultList = mongoTemplate.aggregate
(aggregate, TestDoc.class, CountDto.class).getMappedResults();
最后又加了一个 project 的主要目的是给最终 group 完的结果中的字段加自定义别名,比如像第 2 节的脚本所示,第二次 group 时指定 _id 为 month,但到了代码中,试验过用 month 接收不到,所以用 andExpression
+ as
强行再给 _id 加个别名。
还有,如果加了这个 project 用于指定自定义别名,就得把所有的字段都重新指定一遍,否则结果就缺失了。