Mongodb聚合查询Spring实现

0.需求

对多天的温、湿、照度的实时数据进行按日求平均值

1.数据格式

1.1 原始数据格式

{
  "_id": "5edeec79a74c48f175774db1",
  "_class": "com.wch.pf.base.common.aiot.entry.AppCallbackMessage",
  "hubId": "161003C3",
  "type": "Report",
  "time": "2020-06-09T01:56:50.000Z",
  "deviceId": "11119100001",
  "data": {
    "time": 1591667810000,
    "humidity": 944,
    "light": 89,
    "temperature": 273
  }
}

在这里插入图片描述

1.2 输出结果

{
  "_id": "2020-06-08",
  "light": 34.25,
  "humidity": 916.5,
  "temperature": 266.75
}

在这里插入图片描述

2.SQL实现


db.data.aiots.message.aggregate([
    { 
        "$match": { 
            "deviceId": "11119100001" ,
            time: {
                $gte: ISODate("2020-06-08T12:36:33.000Z")
            }
        } 
        
    }, 
    {
        $project : {
            //时区数据校准,8小时换算成毫秒数为8*60*60*1000=288000后分割成YYYY-MM-DD日期格式便于分组
            day : {$substr: [{"$add":["$time", 28800000]}, 0, 10] },
            "data": 1 // 设置原有data字段可用,用于计算总价
      
        },
     },
    { 
        "$group": { 
            "_id": "$day", 
            "light": {$avg: "$data.light"},
            "humidity": {$avg: "$data.humidity"},
            "temperature": {$avg: "$data.temperature"}
        } 
    }
]);

3.Java代码实现

Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.match(criteria)
                , Aggregation.project("data","time")
                        .andExpression("time + 28800000").substring(0,10).as("day")
                , Aggregation.group("day")
                        .avg("data.temperature").as("temperature")
                        .avg("data.humidity").as("humidity")
                        .avg("data.light").as("light"));

        AggregationResults<Map> aggregate = mongoTemplate.aggregate(aggregation, "data.aiots.message", Map.class);

代码解读

  1. $match用于匹配满足条件的文档,如同find函数。

  2. $project用于指示字段是否输出以及字段输出控制。

  3. $substr$add,一个是分割操作,另一个相加操作。不难发现我们的原始数据created_at字段是具体到秒,因此如果想根据日期进行分割的话,那么需要将created_at分割成我们想要的日期格式,这其中需要特别注意的是mongoodb存储的数据是按照世界时存储的,因此进行分割操作时候需要对时间进行时区校正,因此需使用**add8()便add**加上时区差8小时(毫秒数)才能得到正确的数据,最后一步便是利用**group**进行分组了。

    细心的小伙伴可以会发现aggregate自带日期操作**year,year,month,dayOfMonthyyyyMMddfiddingappregate使dayOfMonth**用于获取年,月,日,会想着通过这三个参数来拼装成**yyyy-MM-dd**日期格式,可惜,fidding之前也是这么操作的,只是最后发现appregate并解析不了,故在此使用了**substr**分割方法。

  4. $group分组以及统计,其中**_id对应值便是我们所需分组的字段数据,totalPrice则是用$sum对同组数据的字段price进行求和,并将结果存放于totalPrice**中。

  5. $sum字段求和。

  6. $sort排序。

4.参考资料

mongoose聚合aggregate按日期分组计算
mongodb-Spring官方文档
Java Code Examples for org.springframework.data.mongodb.core.aggregation.Aggregation.newAggregation()

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章