Mongodb高级查询

1. 高级查询

对基础语法还不了解的,可以去此网站MongoDB语法进行学习,语法比较全面。

1.1 复杂find

1.2 复杂aggregate

示例一:对匹配到的数据分组

示例二:先match匹配,再project处理数据格式,最后根据多个条件group聚合

db.deviceDataCollection.aggregate([{
    "$match": {
        "deviceId": "232424",
        "deviceType": "2",
        "commonDeviceData.commonDeviceExtraData": {
            "$exists": true
        },
        "$or": [{
            "commonDeviceData.commonDeviceExtraData.identityNo": "2455266x"
        }, {
            "commonDeviceData.commonDeviceExtraData.identityNo": "2455266X"
        }]
    }
}, {
    "$project": {
        "date": {
            "$dateToString": {
                "format": "%Y-%m-%d",
                "date": "$ts"
            }
        },
        "commonDeviceData": 1
    }
}, {
    "$group": {
        "_id": {
            "date": "$date",
            "extraCode": "$commonDeviceData.extraCode"
        },
        "checkTime": {
            "$last": "$date"
        },
        "commonDeviceData": {
            "$last": "$commonDeviceData"
        }
    }
}])

在这里插入图片描述

1.3 格式转换

$convert 是 MongoDB 中的一个聚合管道运算符,用于将一个数据类型转换为另一个数据类型。转换类型包括数字类型、日期类型、字符串类型等。
基本语法:

{ $convert: { input: <expression>, to: <type>, onError: <expression>, onNull: <expression> } }

其中,input 参数表示要转换的值;to 参数表示要转换的目标类型,可以为以下类型之一:double、string、objectId、bool、date、int、long;onError 和 onNull 参数是可选参数,分别表示在转换过程中遇到错误或输入为 null 时返回的值

        "heartRate": {
            "$convert": {
                "input": "$commonDeviceData.commonDeviceExtraData.heartRate",
                "to": "int",
                "onError": 0,
                "onNull": 0
            }
        }

java代码

Aggregation.project()
        .and(ConvertOperators.Convert.convertValueOf("commonDeviceData.commonDeviceExtraData.heartRate")
        .to("int")
        .onErrorReturn(0)
        .onNullReturn(0))
        .as("heartRate")

1.4 日期处理

1.4.1 add、subtract、multiply、divide、mod

基础语法:加、减、乘、除、取模是类似的

{ $add[ < expression1 >< expression2 >... ] }
{ $subtract: [ <expression1>, <expression2> ] }
{ $multiply[ < expression1 >< expression2 >... ] }
{ $divide: [ <expression1>, <expression2> ] }
{ $mod: [ <expression1>, <expression2> ] }

主要是嵌套,需求不可能只是简单的加减乘除。如果函数是最内层的,就用[]包裹,如果不是最内层,则哪一项中有函数,就加一层{}包裹。

{
    "$mod": [{
        "$subtract": [
            "$ts",
            ISODate("1970-01-01T00:00:00Z")
        ]
    }, 600000]
}

1.4.2 日期计算

上述公式在用于日期计算的时候,会自动将日期转换成时间戳进行计算。就比如上面的示例,我们将ts这个时间字段减去1970年,得到的便是这段时间对应的毫秒值。
比如我们有个需求,我们需要每10分钟聚合一次数据,求这十分钟的呼吸、心率的平均值。关键在于如何按每10分钟进行分组,如果是按每分钟分组,我们可以用MongoDB中的日期运算符或者对时间进行截取,去除分钟之后的部分,按处理后的时间进行分组。简单的来想,现在有一个小时的数据,从1分钟到60分钟,如果分成6份,我们将分钟与10取mod值,然后用自身减去这个模值就可以了,得到的便是0、10、20、30、40、50,然后我们根据这个计算值分组就可以了。我们常用1970-01-01 00:00:00作为起始时间,方便我们取值。
关键一:处理分组字段,((数据时间 - 1970)-(数据时间 - 1970)/ 10分钟 )= 当前这组数据起始十分钟的时间戳。

 "date": {
            "$subtract": [{
                "$subtract": [
                    "$ts",
                    ISODate("1970-01-01T00:00:00Z")
                ]
            }, {
                "$mod": [{
                    "$subtract": [
                        "$ts",
                        ISODate("1970-01-01T00:00:00Z")
                    ]}
                , 600000]
            }]
        }

关键二:将时间戳再转成日期格式,直接与起始时间1970年相加即可。

  "date": {
            "$add": [
                ISODate("1970-01-01T00:00:00Z")
            , "$date"]
        }

完整sql

db.deviceDataCollection_1537267276171177984.aggregate([{
    "$match": {
        "deviceId": "xxxxxxx",
        "ts": {
            "$gte": ISODate("2023-09-12T01:21:37Z"),
            "$lte": ISODate("2023-09-13T01:21:37Z")
        }
    }
}, {
    "$project": {
        "date": {
            "$subtract": [{
                "$subtract": [
                    "$ts",
                    ISODate("1970-01-01T00:00:00Z")
                ]
            }, {
                "$mod": [{
                    "$subtract": [
                        "$ts",
                        ISODate("1970-01-01T00:00:00Z")
                    ]}
                , 600000]
            }]
        },
        "status": "$commonDeviceData.commonDeviceExtraData.status",
        "heartRate": {
            "$convert": {
                "input": "$commonDeviceData.commonDeviceExtraData.heartRate",
                "to": "int",
                "onError": 0,
                "onNull": 0
            }
        },
        "respiratoryRate": {
            "$convert": {
                "input": "$commonDeviceData.commonDeviceExtraData.respiratoryRate",
                "to": "int",
                "onError": 0,
                "onNull": 0
            }
        }
    }
}, {
    "$group": {
        "_id": "$date",
        "heartRateAvg": {
            "$avg": "$heartRate"
        },
        "breathAvg": {
            "$avg": "$respiratoryRate"
        },
        "status": {
            "$last": "$status"
        },
        "date": {
            "$last": "$date"
        }
    }
}, {
    "$project": {
        "date": {
            "$add": [
                ISODate("1970-01-01T00:00:00Z")
            , "$date"]
        },
        "heartRateAvg": 1,
        "breathAvg": 1,
        "status": 1
    }
}, {
    "$sort": {
        "date": - 1
    }
}])

结果:其中_id 则是date对应的时间戳。
在这里插入图片描述
java代码

        // 聚合
        Aggregation agg = Aggregation.newAggregation(
                Aggregation.match(criteria),
                Aggregation.project() 
                        .andExpression("{$subtract:{{$subtract:{'$ts',new java.util.Date(0L)}},{$mod:{{$subtract: {'$ts',new java.util.Date(0L)}},1000 * 60 * 10}}}}").as("date")
                        .and("commonDeviceData.commonDeviceExtraData.status").as("status")
                        .and(ConvertOperators.Convert.convertValueOf("commonDeviceData.commonDeviceExtraData.heartRate")
                                .to("int")
                                .onErrorReturn(0)
                                .onNullReturn(0))
                        .as("heartRate")
                        .and(ConvertOperators.Convert.convertValueOf("commonDeviceData.commonDeviceExtraData.respiratoryRate")
                                .to("int")
                                .onErrorReturn(0)
                                .onNullReturn(0))
                        .as("respiratoryRate"),
                Aggregation.group("date")
                        .avg("heartRate").as("heartRateAvg")
                        .avg("respiratoryRate").as("breathAvg")
                        .last("status").as("status")
                        .last("date").as("date"),
                Aggregation.project()
                        .andExpression("{$add:{new java.util.Date(0L),'$date'}}").as("date")
                        .and("heartRateAvg").as("heartRateAvg")
                        .and("breathAvg").as("breathAvg")
                        .and("status").as("status")
        ).withOptions(allowDiskOptions);

代码注意点:

  1. 代码用的andExpression表达式,表达式里面不支持中括号,我们需要将其转变成大括号
  2. 表示1970-01-01 00:00:00时,需要用到new Date()函数,表达式中的方法一定要用全类名表示,比如这里的new java.util.Date()
  3. new java.util.Date(0L)中的参数是long类型的,否则会出现本地运行没问题,但是Liunx系统中报错。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值