mongodb在Java中条件分组聚合查询并且分页(时间戳,按日期分组,年月日...)

废话不多说,先看效果图:

  • SQL查询结果示例:
  • 多种查询结果示例:

原SQL:

sql 复制代码
db.getCollection("hbdd_order").aggregate([
    {
    // 把时间戳格式化
        $addFields: {
            orderDate: {
								"$dateToString": {
										"format": "%Y-%m-%d",
										"date": {
												"$toDate": "$hzdd_order_addtime"
										}
								}
            }
        }
    },
    {
        $match: {
            // 筛选条件
            hzdd_order_addtime: {
                $gte: 1722441600000,
                $lt: 1725120000000
            }
        }
    },
    {
    // 按格式过的时间分组
        $group: {
        "_id": "$orderDate",
            paidAmount: {
                $sum: { // 统计
                    $cond: [{ // 条件类似if true =1 else =0
                        $eq: ["$hzdd_order_ispay", 1]
                    }, "$hzdd_order_amount", 0]
                }
            },
            paidCount: {
                $sum: {
                    $cond: [{
                        $eq: ["$hzdd_order_ispay", 1]
                    }, 1, 0]
                }
            },
            unpaidAmount: {
                $sum: {
                    $cond: [{
                        $eq: ["$hzdd_order_ispay", 0]
                    }, "$hzdd_order_amount", 0]
                }
            },
            unpaidCount: {
                $sum: {
                    $cond: [{
                        $eq: ["$hzdd_order_ispay", 0]
                    }, 1, 0]
                }
            }
        }
    },
    {
        $project: {
            date: "$_id",
            paidAmount: 1,
            paidCount: 1,
            unpaidAmount: 1,
            unpaidCount: 1
        }
    },
    {
        $sort: { // 排序
            date: 1
        }
    }
]);

Java语句:

代码中多了些内容,但是和SQL语句大差不差

(懒得替换类名,大家看到陌生的类就是自己建的)

java 复制代码
import com.mongodb.client.result.UpdateResult;
import jodd.util.StringUtil;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.*;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

public Page<OrderStatVo> orderStatistical(OrderStatQuery query) { 
        Pageable pageable = PageRequest.of(query.getPageNum() - 1, query.getPageSize());
        MongoTemplate mongoTemplate = mongoFactory.mongoTemplate(OrderConstants.ORDER_DB);

        // 时间筛选
        Long startTime = query.getStartTime();
        Long endTime = query.getEndTime();
        // 区分 1年,2月,3日
        int type = query.getType();
        // 按商家id
        String shopId = query.getShopId();
        // 按code筛选
        Integer areaCode = query.getAreaCode();
        Integer provinceCode = query.getProvinceCode();
        Integer cityCode = query.getCityCode();
        Integer countyCode = query.getCountyCode();

        // 基础匹配条件:按年初和年末 时间戳
        Criteria baseCriteria = new Criteria();
        // 额外的筛选条件
        List<Criteria> additionalCriteria = new ArrayList<>();

        if (startTime != null && endTime != null) {
            additionalCriteria.add(Criteria.where("hzdd_order_addtime").gte(startTime).lt(endTime));
        }
        if (StringUtil.isNotEmpty(shopId)) {
            additionalCriteria.add(Criteria.where("hzdd_order_sjid").is(shopId));
        }
        if (areaCode != null && areaCode != 0) {
            additionalCriteria.add(Criteria.where("hzdd_order_area_code").is(areaCode));
        }
        if (provinceCode != null && provinceCode != 0) {
            additionalCriteria.add(Criteria.where("hzdd_order_province_code").is(provinceCode));
        }
        if (cityCode != null && cityCode != 0) {
            additionalCriteria.add(Criteria.where("hzdd_order_city_code").is(cityCode));
        }
        if (countyCode != null && countyCode != 0) {
            additionalCriteria.add(Criteria.where("hzdd_order_county_code").is(countyCode));
        }

        // 合并所有条件
        if (!additionalCriteria.isEmpty()) {
            baseCriteria.andOperator(additionalCriteria.toArray(new Criteria[0]));
        }

        // 构建匹配操作
        MatchOperation matchOperation = Aggregation.match(baseCriteria);

        // 添加字段操作,将 Unix 时间戳转换为日期字符串
        String expression = switch (type) {
            case 1 -> "{$dateToString: { format: '%Y', date: { $toDate: '$hzdd_order_addtime' }}}";
            case 2 -> "{$dateToString: { format: '%Y-%m', date: { $toDate: '$hzdd_order_addtime' }}}";
            case 3 -> "{$dateToString: { format: '%Y-%m-%d', date: { $toDate: '$hzdd_order_addtime' }}}";
            default -> "{$dateToString: { format: '%Y-%m-%d', date: { $toDate: '$hzdd_order_addtime' }}}";
        };
        AddFieldsOperation addFieldsOperation = Aggregation.addFields().addField("orderDate")
                .withValueOfExpression(expression).build();

        // 分组操作
        GroupOperation groupOperation = Aggregation.group("orderDate")
                .sum(ConditionalOperators.Cond.when(Criteria.where("hzdd_order_ispay").is(1))
                        .then("$hzdd_order_amount").otherwise(0)).as("paidAmount")
                .sum(ConditionalOperators.Cond.when(Criteria.where("hzdd_order_ispay").is(1))
                        .then(1).otherwise(0)).as("paidCount")
                .sum(ConditionalOperators.Cond.when(Criteria.where("hzdd_order_ispay").is(0))
                        .then("$hzdd_order_amount").otherwise(0)).as("unpaidAmount")
                .sum(ConditionalOperators.Cond.when(Criteria.where("hzdd_order_ispay").is(0))
                        .then(1).otherwise(0)).as("unpaidCount");

        // 投影操作
        ProjectionOperation projectionOperation = Aggregation.project()
                .and("_id").as("date")
                .andInclude("paidAmount", "paidCount", "unpaidAmount", "unpaidCount");

        // 排序操作
        SortOperation sortOperation = Aggregation.sort(Sort.Direction.ASC, "date");

        // 分页操作
        SkipOperation skipOperation = Aggregation.skip((long) pageable.getPageNumber() * pageable.getPageSize());
        LimitOperation limitOperation = Aggregation.limit(pageable.getPageSize());

        // 构建不包含分页的聚合查询以获取总条数
        Aggregation countAggregation = Aggregation.newAggregation(
                matchOperation,
                addFieldsOperation,
                groupOperation,
                Aggregation.group("orderDate").count().as("totalCount"), // 添加计数操作
                Aggregation.project("totalCount").andExclude("_id") // 只包含 totalCount 字段
        );
        // 执行聚合查询以获取总条数
        AggregationResults<Document> totalCountResults = mongoTemplate.aggregate(countAggregation, "hbdd_order", Document.class);
        Document document = totalCountResults.getMappedResults().stream().findFirst().orElse(null);
        int total = document != null ? (int) document.get("totalCount") : 0;

        // 构建包含分页的聚合查询
        Aggregation aggregation = Aggregation.newAggregation(
                matchOperation,
                addFieldsOperation,
                groupOperation,
                projectionOperation,
                sortOperation,
                skipOperation,
                limitOperation
        );
        // 第二个参数是文档名(表名),第三个参数是接收的类,字段对应上面代码中的as别名字段
        AggregationResults<OrderStatVo> results = mongoTemplate.aggregate(aggregation, "hbdd_order", OrderStatVo.class);
        List<OrderStatVo> everyDayOrderStats = results.getMappedResults();
        // 分页操作
        return new PageImpl<>(everyDayOrderStats, pageable, total);
    }

** OrderStatQuery 类就不展示了,就是传值进来的筛选条件 **

OrderStatVo类
java 复制代码
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;

@Data
@Schema(description = "订单统计")
public class OrderStatVo {
    @Schema(description = "周期")
    private String date;
    @Schema(description = "已支付金额")
    private Double paidAmount;
    @Schema(description = "已支付订单数")
    private Long paidCount;
    @Schema(description = "未支付金额")
    private Double unpaidAmount;
    @Schema(description = "未支付订单数")
    private Long unpaidCount;

}
Java中使用mongoDB小技巧:

配置文件中加上下面这行,可以打印出mongo的SQL语句

yaml 复制代码
logging:
  level:
    org.springframework.data.mongodb.core.MongoTemplate: DEBUG
相关推荐
瓜牛_gn29 分钟前
mysql特性
数据库·mysql
奶糖趣多多2 小时前
Redis知识点
数据库·redis·缓存
阿伟*rui2 小时前
配置管理,雪崩问题分析,sentinel的使用
java·spring boot·sentinel
CoderIsArt3 小时前
Redis的三种模式:主从模式,哨兵与集群模式
数据库·redis·缓存
XiaoLeisj3 小时前
【JavaEE初阶 — 多线程】单例模式 & 指令重排序问题
java·开发语言·java-ee
paopaokaka_luck3 小时前
【360】基于springboot的志愿服务管理系统
java·spring boot·后端·spring·毕业设计
dayouziei3 小时前
java的类加载机制的学习
java·学习
师太,答应老衲吧5 小时前
SQL实战训练之,力扣:2020. 无流量的帐户数(递归)
数据库·sql·leetcode
Yaml45 小时前
Spring Boot 与 Vue 共筑二手书籍交易卓越平台
java·spring boot·后端·mysql·spring·vue·二手书籍
小小小妮子~5 小时前
Spring Boot详解:从入门到精通
java·spring boot·后端