添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

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 用于指定自定义别名,就得把所有的字段都重新指定一遍,否则结果就缺失了。