服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

Mysql|Sql Server|Oracle|Redis|MongoDB|PostgreSQL|Sqlite|DB2|mariadb|Access|数据库技术|

服务器之家 - 数据库 - MongoDB - MongoDB时间戳转日期及日期分组实例代码

MongoDB时间戳转日期及日期分组实例代码

2023-05-17 13:48耳冉鹅 MongoDB

时间戳(timestamp)通常是一个字符序列,唯一地标识某一刻的时间,下面这篇文章主要给大家介绍了关于MongoDB时间戳转日期及日期分组的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下

前言

最近遇到的一个数据统计折线图的性能优化点,可以说是一定思维上的转变,就记录下咯

背景:cron定时任务读取当前统计数据的异常值,频率为每五分钟记录一次,折线图要求获取每日的异常项峰值

最一开始的想法:将数据读取到内存中进行条件过滤、计算

首先根据时间戳将数据以日期作为分组,其次在每个分组中获取异常项的峰值数据,时间复杂度O(n*n),最好以日期分组列表+峰值数据列表作为对象返回结果

遇到性能问题:一天的数据量为(60/5)*24=288,默认日期为15天,则统计的数据量为4230,接口返回甚至需要8、9秒的时间,作为一个项目的门面折线图,这种情况 达咩!

优化的念头:我要拿每天的峰值数据,怎么才能直接取到每天的峰值呢,mongo的聚合是不是可以做到啊? $group可以按日期做分组, $max可以拿到最大值,接下来一个 $sort好像是就成了吧! 说干就干!!

接下来的聚合语句均为mongo pipeline,最后附上golang的bson条件哈

?
1
2
3
4
5
6
7
// ResultCountModel _
type ResultCountModel struct {
    CommonBase `json:",inline" yaml:",inline" bson:",inline"`
    ErrorCount int   `json:"error_count" bson:"error_count"`
    Timestamp  int64 `json:"timestamp" bson:"timestamp"`
    MaxTime    int64 `json:"max_time" bson:"max_time"`
}

数据结构定义如上,这里使用CommonBase,是因为在$group聚合后会得到_id唯一标识字段,因此便于获取最后的聚合结果,在定义结构体时将其加上;timestamp单位为毫秒

1、日期筛选

第一步,毫无疑问,对时间戳timestamp进行日期的过滤

?
1
2
3
4
5
6
7
8
{
  $match: {
      timestamp: {
          $gte: 1671897600000, // min_timestamp
          $lt: 1673280000000   // max_timestamp
      }
  }
}
  • $gte 大于等于
  • $lt 小于

2、日期转换

第二步,根据时间戳大小进行日期的转换,这里是用的是$project, 将具有请求字段的文档传递到管道中的下一阶段。指定的字段可以是输入文档中的现有字段或是新计算的字段

使用$project主要思路是,将timestamp时间戳转换为标准日期,之后输出为想要的format形式;同时使用 $project保留需要的字段

时间戳转换日期

核心方法:$dateToString

?
1
2
3
4
5
6
7
8
{
  $dateToString: {
    date: <dateExpression>,
    format: <formatString>,
    timezone: <tzExpression>,
    onNull: <expression>
  }
}
  • date :要转换的字符串日期,必须是解析为Date、Timestamp、ObjectID 的有效表达式
  • format: 日期格式规范
  • timezone:运算结果的时区,常用UTC偏移量
  • onNull: date为null或缺失时要返回的值

日期格式想要“月份-日期”,那format: “%m-%d”

日期数据这里,如果直接使用输入文档中的现有字段的话 date: “$timestamp”,则会报错:PlanExecutor error during aggregation :: caused by :: can’t convert from BSON type long to Date

因此我们需要将时间戳转换为日期: 即格林威治开始时间(1970-01-01 00:00:00)+时间戳+时差

?
1
2
3
4
5
6
7
date:{
  $add:[
    new Date(0),
    "$timestamp",
    28800000
  ]
},

注意:

  • MongoDB时间的基本单位为毫秒,所以本文直接使用”$timestamp”即可;若时间单位为秒级时,则需要使用 $multiply进行乘法运算:{ $multiply:[" $timestamp”,1000]}
  • MongoDB是UTC时区,即中时区(0度经线), 中国为东八区,因此需要使用timezone添加8小时(即28800000毫秒)

pipeline如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
day:{
  $dateToString:{
    format:"%m-%d",
    date:{
      $add:[
        new Date(0),
        "$timestamp",
        28800000
      ]
    },
  }
},

保留需要字段

?
1
2
3
4
5
6
7
8
/**
 * specifications: The fields to
 *   include or exclude.
 */
{
  timestamp:1,
  error_count:1,
}

$project将保留字段置为1即可进行数据保留操作

第二步完整pipeline如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
    $project: {
        day: {
            $dateToString: {
                format: '%m-%d',
                date: {
                    $add: [
                        ISODate('1970-01-01T00:00:00.000Z'),
                        '$timestamp',
                        28800000
                    ]
                }
            }
        },
        timestamp: 1,
        error_count: 1
    }
},

3、日期分组

第三步,使用$group进行日期分组

?
1
2
3
4
5
6
7
8
{
  $group:
    {
      _id: <expression>, // Group key
      <field1>: { <accumulator1> : <expression1> },
      ...
    }
 }
  • _id: 表达式指定组密钥
  • field: 计算使用的累加器运算符

这里我们需要将第二步获得的日期转换进行分组聚合,同时获取每个分组的异常项最大值即峰值数据

?
1
2
3
4
5
6
7
8
9
10
11
{
    $group: {
        _id: '$day',
        error_count: {
            $max: '$error_count'
        },
        max_time: {
            $max: '$timestamp'
        }
    }
},

这里额外获取了max_time字段,主要用于在计算统计数据时的排序,在最后排序部分会使用到

4、日期排序

这里做一个假设,如果不使用max_time的话,如何将数据进行按日期的排序呢? 如果根据_id进行排序,则会出现“上年末”排序在“下年初”的情况(感谢现在的日期,不然会忘记这个问题)

所以将每个分组的最大时间戳保留下来时很有必要的!

这里取$max $min都是可以的哈

?
1
2
3
4
5
{
    $sort: {
        max_time: 1
    }
}

最终完整pipeline:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
[{
 $match: {
  timestamp: {
   $gte: 1671897600000,
   $lt: 1673280000000
  }
 }
}, {
 $project: {
  day: {
   $dateToString: {
    format: '%m-%d',
    date: {
     $add: [
      ISODate('1970-01-01T00:00:00.000Z'),
      '$timestamp',
      28800000
     ]
    }
   }
  },
  timestamp: 1,
  error_count: 1
 }
}, {
 $group: {
  _id: '$day',
  error_count: {
   $max: '$error_count'
  },
  max_time: {
   $max: '$timestamp'
  }
 }
}, {
 $sort: {
  max_time: 1
 }
}]
[{
 $match: {
  timestamp: {
   $gte: 1671897600000,
   $lt: 1673280000000
  }
 }
}, {
 $project: {
  day: {
   $dateToString: {
    format: '%m-%d',
    date: {
     $add: [
      ISODate('1970-01-01T00:00:00.000Z'),
      '$timestamp',
      28800000
     ]
    }
   }
  },
  timestamp: 1,
  error_count: 1
 }
}, {
 $group: {
  _id: '$day',
  error_count: {
   $max: '$error_count'
  },
  max_time: {
   $max: '$timestamp'
  }
 }
}, {
 $sort: {
  max_time: 1
 }
}]

在golang里面,Aggregate则直接使用pipeline即可,亦可转换为filter使用

filter代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
filter := bson.A{
        bson.D{{"$match", bson.D{
            {"timestamp", bson.D{
                {"$gte", param.MinTimestamp},
                {"$lt", param.MaxTimestamp},
            }}}},
        },
        bson.D{{"$project", bson.D{
            {"day", bson.D{
                {"$dateToString", bson.D{
                    {"format", "%m-%d"},
                    {"date", bson.D{
                        {"$add", bson.A{
                            time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC),
                            "$timestamp",
                            28800000,
                        }},
                    }},
                }},
            }},
            {"error_count", 1},
            {"timestamp", 1},
        }}},
        bson.D{{"$group", bson.D{
            {"_id", "$day"},
            {"max_time", bson.D{{"$max", "$timestamp"}}},
            {"error_count", bson.D{{"$max", "$error_count"}}},
        }}},
        bson.D{{"$sort", bson.D{{"max_time", 1}}}},
    }

完结撒花

补充:解决MongoDB存储时间时差的问题

MongoDB存储时间类型数据时,都是先转换为UTC时间,然后存储到数据库中,当我们取出存储的时间时,就会出现时差的问题。比如我们用的北京时间,读取到的数值就会看到比当前时间少了8个小时,难道说我们在每次读取的时候都要单独处理一下时间吗,这就比较麻烦。其实,我们可以在存储的时候进行相应的处理,只需使用getTimezoneOffset()和toISOString()函数。

getTimezoneOffset函数:返回此地区的时差(当地时间与GMT格林威治标准时间的地区时差),单位为分钟。

?
1
2
3
4
5
6
<script>
  // 我们是东八区
  var d = new Date();
  var tz = d.getTimezoneOffset();
  console.log(tz);  // -480
</script>

toISOString()函数:使用ISO标准将 Date 对象转换为字符串。

该标准称为 ISO-8601 ,格式为: YYYY-MM-DDTHH:mm:ss.sssZ。

封装时间转换函数

?
1
2
3
4
5
localDate(v) {
    const d = new Date(v || Date.now());
    d.setMinutes(d.getMinutes() - d.getTimezoneOffset());
    return d.toISOString();
},

我们在存储时间的时候调用localDate()这个函数就可以了,无论你处在哪个时区结果显示都和当地时间一样。

总结

到此这篇关于MongoDB时间戳转日期及日期分组的文章就介绍到这了,更多相关MongoDB时间戳转日期内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/qq_45366447/article/details/128617142

延伸 · 阅读

精彩推荐
  • MongoDBMongoDB超大块数据问题解决

    MongoDB超大块数据问题解决

    这篇文章主要为大家介绍了MongoDB超大块数据问题的技术瓶颈解决方法,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪...

    哪吒编程8522023-02-14
  • MongoDBMongoDB教程之索引介绍

    MongoDB教程之索引介绍

    这篇文章主要介绍了MongoDB教程之索引介绍,本文讲解了索引基础、唯一索引、使用explain、索引管理等内容,需要的朋友可以参考下 ...

    MongoDB之家3772020-04-30
  • MongoDBMongoose 在egg中的使用详解

    Mongoose 在egg中的使用详解

    这篇文章主要介绍了Mongoose 在egg中的使用,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...

    YoLinDeng4642020-06-15
  • MongoDBMongoDB聚合功能浅析

    MongoDB聚合功能浅析

    这篇文章主要介绍了MongoDB聚合功能,需要的朋友可以参考下 ...

    shichen20143182020-04-25
  • MongoDBMongoDB中文档的更新操作示例详解

    MongoDB中文档的更新操作示例详解

    这篇文章主要给大家介绍了关于MongoDB中文档的更新操作的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,...

    独孤求败5162020-05-16
  • MongoDBMongoDB aggregate 运用篇个人总结

    MongoDB aggregate 运用篇个人总结

    最近一直在用mongodb,有时候会需要用到统计,在网上查了一些资料,最适合用的就是用aggregate,以下介绍一下自己运用的心得 ...

    hh764593997742020-05-09
  • MongoDBMongoDB基础之查询文档

    MongoDB基础之查询文档

    这篇文章介绍了MongoDB基础之查询文档,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...

    springsnow8712022-06-07
  • MongoDBMongoDB的常用命令汇总(Mongo4.2.8)

    MongoDB的常用命令汇总(Mongo4.2.8)

    这篇文章主要介绍了MongoDB的常用命令汇总(Mongo4.2.8),给大家介绍了集合collection相关、用户相关、数据库相关内容,对大家的学习或工作具有一定的参考借鉴...

    fen_fen9242022-09-27