2021 iThome 鐵人賽 - DAY27 MongoDB Time Series Collection

2021 iThome 鐵人賽 MongoDB披荊斬棘之路


DAY27 MongoDB Time Series Collection

什麼是 Time series collection

MongoDB 5.0 發佈的新功能,僅在 5.0 之後的版本提供。
時間序列集合(Time series collection) 是一個新型態的集合,有別於 capped collection一般 Collection,它有諸多限制:

  • 僅支援 Read / Insert
  • 無法 Delete / Update,只能夠 drop collection
  • document 最大 4MB
  • 無法轉為 capped一般 collection,反之亦然
  • 沒辦法設定 Index 的 unique(_id為例外), partial
  • metaField, timeField (後面說明)兩欄位僅有在建立 Collection 才可以決定,之後無法修改。
  • Collection 無法 sharding

看了這麼多限制後,還是不知道什麼是時間序列集合。基本上該功能就可以想像成依照時間來進行統計分析的集合,像是股票資料或天氣資料這種,依照時間進行,然後去統計分析股票價位高低或者溫度的高低。

Time series collection 三大設定

  • timeField
    時間欄位,指定要做為統計的時間欄位,必須是要時間的資料型別

  • metaField
    分群的欄位,統計資料會自動以該欄位進行分群。
    因為只能指定一個欄位,所以可以進行變通,例如原本是 { "name": "Bruce"} 改為 { "name": "Bruce", "lastName": "Lee" }

  • granularity
    統計時間粒度,可以是 seconds, minuteshours

建立 Time series collection

有著前面的知識後我們來準備一個範例,建立語法以及預設資料如下

1
2
3
4
5
6
7
8
db.createCollection("order.ts", {
timeseries: {
timeField: "orderDate",
metaField: "customerName",
granularity: "hours"
},
expireAfterSeconds: 3600*31*6
});

資料範本

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
{
"customerId": 10001,
"customerName": { "name":"Bruce", "lastName": "Lee" },
"orderPrize": 1280,
"orderStatus": 1,
"orderDate": ISODate("2021-09-01T11:00:00.000Z"),
"deliverDate": ISODate("2021-09-05T15:00:00.000Z")
},
{
"customerId": 10001,
"customerName": { "name":"Bruce", "lastName": "Lee" },
"orderPrize": 1880,
"orderStatus": 3,
"orderDate": ISODate("2021-09-01T12:00:00.000Z"),
"deliverDate": ISODate("2021-09-06T16:00:00.000Z")
},
{
"customerId": 10001,
"customerName": { "name":"Bruce", "lastName": "Lee" },
"orderPrize": 540,
"orderStatus": 2,
"orderDate": ISODate("2021-09-02T03:00:00.000Z"),
"deliverDate": ISODate("2021-09-07T17:00:00.000Z")
},
{
"customerId": 10002,
"customerName": { "name":"Old", "lastName": "Chou" },
"orderPrize": 2300,
"orderStatus": 1,
"orderDate": ISODate("2021-09-01T10:00:00.000Z"),
"deliverDate": ISODate("2021-09-07T14:00:00.000Z")
},
{
"customerId": 10003,
"customerName": "Kate",
"orderPrize": 2890,
"orderStatus": 3,
"orderDate": ISODate("2021-09-01T12:00:00.000Z"),
"deliverDate": ISODate("2021-09-07T14:00:00.000Z")
}

查看 time series collection

1
2
3
4
timeseries> show collections
order.ts [time-series]
system.buckets.order.ts
system.views

建立完後我們看到除了原本的 order.ts collection 有被標註為 [time-series],還多了兩個:

  • system.views
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"_id" : "timeseries.order.ts",
"viewOn" : "system.buckets.order.ts",
"pipeline" : [
{
"$_internalUnpackBucket" : {
"timeField" : "orderDate",
"metaField" : "customerName",
"bucketMaxSpanSeconds" : 2592000,
"exclude" : []
}
}
]
}

裡面存放了 bukets 的 view,與建立的資訊

  • system.buckets.order.ts

這裡總共有四筆資料,分別是按照 metaField 作為 group 統計而來的,礙於篇幅,我舉其中一個例子並分段來看

Part 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"_id" : ObjectId("612ec2808b826f0fc8ce4afa"),
"control" : {
"version" : 1,
"min" : {
"_id" : ObjectId("614eee10ebdbba2a6c81adbd"),
"customerId" : 10001.0,
"orderPrize" : 540.0,
"orderStatus" : 1.0,
"orderDate" : ISODate("2021-09-01T00:00:00.000Z"),
"deliverDate" : ISODate("2021-09-05T15:00:00.000Z")
},
"max" : {
"_id" : ObjectId("614eee10ebdbba2a6c81adbf"),
"customerId" : 10001.0,
"orderPrize" : 1880.0,
"orderStatus" : 3.0,
"orderDate" : ISODate("2021-09-02T03:00:00.000Z"),
"deliverDate" : ISODate("2021-09-07T17:00:00.000Z")
}
},

這邊直接幫你算出 metaField { "name":"Bruce", "lastName": "Lee" } 分類裡的每個項目的最小值與最大值,要是能設定多算一些統計值也是不錯吧。

有個小疑問是最小值的 orderDate 值是 ISODate("2021-09-01T00:00:00.000Z") 但原始數據明明是 ISODate("2021-09-01T10:00:00.000Z"),可能會是個 bug?

Part 2

1
2
3
4
"meta" : {
"lastName" : "Lee",
"name" : "Bruce"
},

就是剛開始建立 Collection 時指定用來分群的 metaField

Part3

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
"data" : {
"orderDate" : {
"0" : ISODate("2021-09-01T11:00:00.000Z"),
"1" : ISODate("2021-09-01T12:00:00.000Z"),
"2" : ISODate("2021-09-02T03:00:00.000Z")
},
"customerId" : {
"0" : 10001.0,
"1" : 10001.0,
"2" : 10001.0
},
"deliverDate" : {
"0" : ISODate("2021-09-05T15:00:00.000Z"),
"1" : ISODate("2021-09-06T16:00:00.000Z"),
"2" : ISODate("2021-09-07T17:00:00.000Z")
},
"orderPrize" : {
"0" : 1280.0,
"1" : 1880.0,
"2" : 540.0
},
"_id" : {
"0" : ObjectId("614eee10ebdbba2a6c81adbd"),
"1" : ObjectId("614eee10ebdbba2a6c81adbe"),
"2" : ObjectId("614eee10ebdbba2a6c81adbf")
},
"orderStatus" : {
"0" : 1.0,
"1" : 3.0,
"2" : 2.0
}
}

從這邊可以看到 data 這個項目裡面存放的就是各個欄位的原始數據,如果要做聚合、統計都能夠從這邊直接取用,順序也是按照寫入的順序。


看到這邊我就不再繼續延伸使用 aggregation 示範內容了,基本上 time series collection 就是幫你把資料按照另一個格式進行儲存,讓你可以更快的在感興趣的欄位進行統計。

另外因為是 demo 概念,沒有建立 index,請務必在使用時加上相對應得索引,使你的查詢更加有效率。
例如幫你自動分桶的集合 system.buckets.order.ts 能這樣做,或者是原本的 order.ts 集合。

  • 作者: MingYi Chou
  • 版權聲明: 轉載不用問,但請註明出處!本網誌均採用 BY-NC-SA 許可協議。