SpringBoot+mongodb实现分组统计、时间范围查询、去重排序查询、组合排序、分页查询

SpringBoot+mongodb实现分组统计、时间范围查询、去重排序查询、组合排序

一、分组统计(Aggregation)

1.按某字段分组统计

    /**
     * 根据分类名称 分组统计销售数量
     * @param dataType 1:本日 2:本月 3:本年
     * @return
     */
    @RequestMapping("/findInfoByGroup")
    public List<Map> findInfoByGroup(){
        //从mongodb里获取满足条件的记录   status:状态 1:上架  2:下架
        Criteria criteria = Criteria.where("status").is(1);
        //聚合函数查询统计信息
        Aggregation aggregation = Aggregation.newAggregation(Aggregation.match(criteria),
                //按分类名称 统计 销售数量
                Aggregation.group("category").sum("sale_num").as("total"),
                //按照total降序
                Aggregation.sort(Sort.Direction.DESC,"total")
        );
        AggregationResults<Map> aggregationResults = mongoTemplate.aggregate(aggregation,Product.class,Map.class);
        List<Map> list = aggregationResults.getMappedResults();

        return list;
    }

category:要分组的字段
sale_num:要统计的字段
total:别名

返回结果:

[
    {
        "_id": "口红",
        "total": 2224.0
    },
    {
        "_id": "面膜",
        "total": 997.0
    },
    {
        "_id": "文具",
        "total": 799.0
    },
    {
        "_id": "男装",
        "total": 675.0
    },
    {
        "_id": "女装",
        "total": 448.0
    }
]

_id本来应该是对应查询的字段,没有处理,spring会将分组字段自动转换为_id

2.多字段分组统计

    /**
     * 根据分类名称和规格 分组统计销售数量
     * @param
     * @return
     */
    @RequestMapping("/findInfoByGroup1")
    public List<Map> findInfoByGroup1(){
        //从mongodb里获取最新的一条记录   status:状态 1:上架  2:下架
        Criteria criteria = Criteria.where("status").is(1);
        //聚合函数查询统计信息
        Aggregation aggregation = Aggregation.newAggregation(Aggregation.match(criteria),
                //按分类名称 统计 销售数量
                Aggregation.group("category","specification").sum("sale_num").as("total"),
                //按照total降序
                Aggregation.sort(Sort.Direction.DESC,"total")
        );
        AggregationResults<Map> aggregationResults = mongoTemplate.aggregate(aggregation,Product.class,Map.class);
        List<Map> list = aggregationResults.getMappedResults();

        return list;
    }

返回结果:

[
    {
        "_id": {
            "category": "口红",
            "specification": "个"
        },
        "total": 2224.0
    },
    {
        "_id": {
            "category": "面膜",
            "specification": "盒"
        },
        "total": 997.0
    },
    {
        "_id": {
            "category": "文具",
            "specification": "套"
        },
        "total": 799.0
    },
    {
        "_id": {
            "category": "男装",
            "specification": "件"
        },
        "total": 675.0
    },
    {
        "_id": {
            "category": "女装",
            "specification": "件"
        },
        "total": 448.0
    }
]

二、时间范围查询

方式一:

    /**
     * 同一个字段 按范围查询
     * @return
     */
    @RequestMapping("/findInfoByTime")
    public List<Product> findInfoByTime(@RequestBody Product product){
        Criteria criteria = new Criteria();
        //创建时间  开始时间  结束时间
        if(!ObjectUtils.isEmpty(product.getStartTime()) || !ObjectUtils.isEmpty(product.getEndTime())){
            criteria = criteria.and("create_time");
            if(!ObjectUtils.isEmpty(product.getStartTime())){
                criteria.gte(product.getStartTime());
            }
            if(!ObjectUtils.isEmpty(product.getEndTime())){
                criteria.lte(product.getEndTime());
            }
        }
        //下面的写法报错:Due to limitations of the org.bson.Document, you can't add a second 'create_time' expression specified as 'create_time : Document{{$lte=Thu Jun 02 12:08:00 CST 2022}}'.
        // Criteria already contains 'create_time : Document{{$gte=Sun May 29 12:08:00 CST 2022}}'.
//        if(!ObjectUtils.isEmpty(product.getStartTime())){
//            criteria = criteria.and("create_time").gte(product.getStartTime());
//        }
//        if(!ObjectUtils.isEmpty(product.getEndTime())){
//            criteria = criteria.and("create_time").lte(product.getEndTime());
//        }
        Query query = Query.query(criteria);
        //排序规则 根据分类倒叙,排序升序
        query.with(Sort.by(
                Sort.Order.desc("category"),
                Sort.Order.asc("sort")
        ));
        List<Product> list=mongoTemplate.find(query,Product.class);
        return list;
    }

方式二:

    /**
     * 时间范围检索
     * @param product
     * @return
     */
    @RequestMapping("/findInfoByTime1")
    public List<Product> testTimeRange(@RequestBody Product product){
        Query query = new Query();
        ArrayList<Criteria> list = new ArrayList<Criteria>();
        if(!ObjectUtils.isEmpty(product.getStartTime())){
            list.add(Criteria.where("create_time").gte(product.getStartTime()));
        }
        if(!ObjectUtils.isEmpty(product.getEndTime())){
            list.add(Criteria.where("create_time").lte(product.getEndTime()));
        }
        Criteria[] arr = new Criteria[list.size()];
        if (arr.length > 0){
            list.toArray(arr);
            Criteria criteria = new Criteria().andOperator(arr);
            query.addCriteria(criteria);
        }
        //排序规则 根据分类倒叙,排序升序
        query.with(Sort.by(
                Sort.Order.desc("category"),
                Sort.Order.asc("sort")
        ));
        List<Product> productList=mongoTemplate.find(query,Product.class);
        return productList;
    }

三、去重排序查询

使用mongoTemplate去重排序查询:

第一种,使用mongoTemplate.findDistinct去重,不支持排序,即使你的query条件带sort排序方法。
mongoTemplate.findDistinct去重,会使排序失效。
优点:查询效率高
缺点:只返回单一字段。不可多字段返回。不能使用排序,不推荐使用

String id ="111";
Query query = new Query();
query.addCriteria(Criteria.where("_id").is(id)).with(Sort.by(Sort.Order.desc("create_time")));
List<Product> list = mongoTemplate.find(query, Product.class);
List<Product> activeCodes = mongoTemplate.findDistinct(query, "name",  "product",Product.class, Product.class);

第二种,使用mongoTemplate.aggregate去重支持排序。推荐使用
优点:可指定返回类型。支持排序
缺点:排序是查询效率会变的非常低

//根据 "category","create_time" 去重统计  使用mongoTemplate.aggregate去重,支持排序。推荐使用 优点:可指定返回类型。支持排序 缺点:排序是查询效率会变的非常低
Aggregation agg = Aggregation.newAggregation(
        // 挑选所需的字段,类似select *,*所代表的字段内容
        Aggregation.project("title", "category","sale_num","create_time"),
        // sql where 语句筛选符合条件的记录
        Aggregation.match(criteria),
        // 分组条件,设置分组字段
        Aggregation.group("category","create_time")
                //first,as里最后包含展示的字段
                .first("title").as("title")
                .first("category").as("category")
                .first("sale_num").as("sale_num")
                .first("create_time").as("create_time")
        ,
        // 排序(根据某字段排序 倒序)
        Aggregation.sort(Sort.by(
                Sort.Order.desc("create_time")
        )),
        // 重新挑选需要字段(上面 as 后的 字段)
        Aggregation.project("title", "category","sale_num"),
        //分页
        Aggregation.skip((long) (Page > 0 ? (Page - 1) * pageSize : 0)),
        Aggregation.limit(pageSize)
);
AggregationResults<Product> results = mongoOperations.aggregate(agg, "product", Product.class);
List<Product> list= results.getMappedResults();

根据某字段去重统计(分组统计) Aggregation 分组、分页

    /**
     * 根据某字段去重统计(分组统计) Aggregation 分组、分页
     * @return
     */
    @RequestMapping("/findDistinctInfo")
    public Map<String,Object>  findDistinctInfo(@RequestParam("page") Integer Page,@RequestParam("pageSize") Integer pageSize){
        Map<String,Object> resultMap = new HashMap<>();
        //从mongodb里获取最新的一条记录
        Criteria criteria = new Criteria();

        //根据 "category","create_time" 去重统计  使用mongoTemplate.aggregate去重,支持排序。推荐使用 优点:可指定返回类型。支持排序 缺点:排序是查询效率会变的非常低
        Aggregation agg = Aggregation.newAggregation(
                // 挑选所需的字段,类似select *,*所代表的字段内容
                Aggregation.project("title", "category","sale_num","create_time"),
                // sql where 语句筛选符合条件的记录
                Aggregation.match(criteria),
                // 分组条件,设置分组字段
                Aggregation.group("category","create_time")
                        //first,as里最后包含展示的字段
                        .first("title").as("title")
                        .first("category").as("category")
                        .first("sale_num").as("sale_num")
                        .first("create_time").as("create_time")
                ,
                // 排序(根据某字段排序 倒序)
                Aggregation.sort(Sort.by(
                        Sort.Order.desc("create_time")
                )),
                // 重新挑选需要字段(上面 as 后的 字段)
                Aggregation.project("title", "category","sale_num"),
                //分页
                Aggregation.skip((long) (Page > 0 ? (Page - 1) * pageSize : 0)),
                Aggregation.limit(pageSize)
        );
        AggregationResults<Product> results = mongoOperations.aggregate(agg, "product", Product.class);
        List<Product> list= results.getMappedResults();
        //统计去重后的 总个数
        Aggregation aggregation = Aggregation.newAggregation(Aggregation.match(criteria),
                //按"category","create_time" 多字段分组去重统计
                Aggregation.group("category","create_time").first("category").as("category")
        );
        AggregationResults<Map> aggregate = mongoTemplate.aggregate(aggregation, Product.class, Map.class);

        resultMap.put("total",aggregate.getMappedResults().size());
        resultMap.put("list",list);
        return resultMap;
    }

返回结果:

{
    "total": 14,
    "list": [
        {
            "id": "{\"category\": \"文具\", \"create_time\": \"2022-06-04 12:10:00\"}",
            "title": "乐普升修正带学生用笔形涂改带实惠装",
            "price": null,
            "category": "文具",
            "specification": null,
            "saleNum": 345,
            "sort": null,
            "remark": null,
            "createTime": null,
            "updateTime": null,
            "status": null,
            "startTime": null,
            "endTime": null
        }
    ]
}

四、组合排序

Criteria criteria = new Criteria();
Query query = Query.query(criteria);
//排序规则 根据分类倒叙,排序升序
query.with(Sort.by(
        Sort.Order.desc("category"),
        Sort.Order.asc("sort")
));

五、分页查询

1.普通的分页

    /**
     * 分页获取数据列表
     * @param page 当前页数
     * @param pageSize 每页显示数量
     * @return
     */
    @RequestMapping("/findInfoByPage")
    public Map<String,Object> findInfoByPage(@RequestParam("page") Integer page,@RequestParam("pageSize") Integer pageSize){
        Map<String,Object> result = new HashMap<>();

        Criteria criteria = new Criteria();
        Query query = Query.query(criteria);
        //排序规则 根据分类倒叙,排序升序
        query.with(Sort.by(
                Sort.Order.desc("category"),
                Sort.Order.asc("sort")
        ));
        //获取总个数
        Long count = mongoTemplate.count(query,Product.class);
        //分页
        PageRequest pageRequest = PageRequest.of(page-1,pageSize);
        //分页=== 也可以用 query.skip(当前页-1).limit(页的大小)
        query.with(pageRequest);

        List<Product> list=mongoTemplate.find(query,Product.class);
        result.put("total",count);
        result.put("list",list);
        return result;
    }

2.分组分页

//从mongodb里获取最新的一条记录
Criteria criteria = new Criteria();

//根据 "category","create_time" 去重统计  使用mongoTemplate.aggregate去重,支持排序。推荐使用 优点:可指定返回类型。支持排序 缺点:排序是查询效率会变的非常低
Aggregation agg = Aggregation.newAggregation(
        // 挑选所需的字段,类似select *,*所代表的字段内容
        Aggregation.project("title", "category","sale_num","create_time"),
        // sql where 语句筛选符合条件的记录
        Aggregation.match(criteria),
        // 分组条件,设置分组字段
        Aggregation.group("category","create_time")
                //first,as里最后包含展示的字段
                .first("title").as("title")
                .first("category").as("category")
                .first("sale_num").as("sale_num")
                .first("create_time").as("create_time")
        ,
        // 排序(根据某字段排序 倒序)
        Aggregation.sort(Sort.by(
                Sort.Order.desc("create_time")
        )),
        // 重新挑选需要字段(上面 as 后的 字段)
        Aggregation.project("title", "category","sale_num"),
        //分页
        Aggregation.skip((long) (Page > 0 ? (Page - 1) * pageSize : 0)),
        Aggregation.limit(pageSize)
);
AggregationResults<Product> results = mongoOperations.aggregate(agg, "product", Product.class);
List<Product> list= results.getMappedResults();

六、(条件1 or 条件2) and 条件3 查询

思路:拆分 (条件1 or 条件2) and 条件3 = (条件1 and 条件3) or (条件2 and 条件3)

    /**
     * (条件1 or 条件2) and 条件3 查询
     * @param name
     */
    @RequestMapping("/findUserListByExample")
    public List<Product> findUserListByExample(@RequestParam("name") String name){
        //(条件1 and 条件3) 
        Criteria criteria = Criteria.where("title").regex(".*?" + name + ".*?")
                .and("status").is(1);
        // (条件2 and 条件3)
        Criteria criteria2 = Criteria.where("category").regex(".*?" + name + ".*?")
                .and("status").is(1);
        // 组合(条件1 and 条件3) or (条件2 and 条件3)
        criteria.orOperator(criteria2);

        Query query = Query.query(criteria);

        //排序规则 根据分类倒叙,价格降序
        query.with(Sort.by(
                Sort.Order.desc("category"),
                Sort.Order.desc("price")
        ));
        List<Product> productList=mongoTemplate.find(query,Product.class);
        return productList;
    }

版权声明:本文为qq_26383975原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。