MongoDB基礎之BSON數據類型

  • 2019 年 10 月 6 日
  • 筆記

MongoDB基礎之BSON數據類型

本部落格參考MongoDB4.2官方文檔。

MongoDB的文檔類似於JSON,JSON是一種簡單的額表示數據的方式,僅包含6種數據類型,分別是:null、布爾、數字、字元串、數組和對象。

雖然這些類型的表現已經足夠強大,但是對於絕大多數應用來說還需要另外一些不可或缺的類型。例如,日期類型、數字類型(只有一種,沒法區分整型和浮點)、正則表達式等。

MongoDB在保留JSON基本的鍵值對特性的基礎上,添加了其他一些數據類型。在不同的程式語言下這些類型的表示有些差異。 下面列出MongoDB通常支援的一些類型,同時說明了在shell中這些類型的表示方法。

每種BSON類型都具有整數和字元串標識符,如下表所示:

類型

整數

別名

備註

說明

Double

1

double

shell中的數字類型

64位浮點數

String

2

string

字元串類型

Object

3

object

對象類型

Array

4

array

數組類型

Binary data

5

binData

shell中不可用

二進位數據類型

Undefined

6

undefined

已過時

未定義類型

ObjectId

7

objectId

對象id類型

Boolean

8

bool

布爾類型

Date

9

date

日期類型

Null

10

null

用於表示空值或者不存在的欄位

Regular Expression

11

regex

正則表達式類型

DBPointer

12

dbPointer

已過時

JavaScript

13

javascript

JavaScript程式碼

Symbol

14

symbol

shell中不可用,已過時

JavaScript(with scope)

15

javascriptWithScope

帶作用域的JavaScript程式碼

32-bit integer

16

int

shell中不可用

32位整數

Timestamp

17

timestamp

時間戳類型

64-bit integer

18

long

shell中不可用

64位整數

Decimal128

19

decimal

3.4版本新增

Min key

-1

minKey

shell中無此類型

最小鍵

Max key

127

maxKey

shell中無此類型

最大鍵

一、類型詳解

下面針對一些常用的數據類型進行詳細說明。

1、Double(64位浮點數)

shell中的數字都是這種類型。

{ 「x」 : 3.14 }  { 「x」 : 3 }

2、String(字元串)

BSON字元串是UTF-8。通常,在序列化和反序列化BSON時,每種程式語言的驅動程式都會從語言的字元串格式轉換為UTF-8。可以輕鬆地將大多數國際字元存儲在BSON字元串中。此外,MongoDB的$regex查詢在正則表達式字元串中支援UTF-8。

字元串類型可以使用sort()方法進行排序,但是sort()是由C++的strcmpAPI實現的,排序可能會錯誤的處理某些字元。

3、Array(數組)

數組是一組值,既可以既可以偶組為有序對象來操作,也可以作為無序對象操作。

數組可以包含不同數據類型的元素,實際上,常規鍵值對支援的值都可以作為數組的元素,甚至是套嵌數組。

文檔中的數組有個特性,就是MongoDB能理解其結構,並指導如何深入數組內部對其內容進行操作。這樣就能用內容對數組進行查詢和構建索引了。

MongoDB可以使用原子更新修改數組中的內容。

值的集合或者列表可以表示成數組。

{ 「x」 : [「a」, 「b」, 「c」]}

4、Binary data(二進位數據)

二進位數據可以由任意位元組的串組成。不過shell中無法使用。

5、Undefined(未定義)

文檔中也可以使用未定義類型undefined。4.2版本中已經顯示過時。

{ 「x」 : unddefined }

6、ObjectId

ObjectId使用12位元組的存儲空間,每個位元組兩位十六進位數字,是一個24位的字元串。

在早期版本中,這些位元組是有特定的結構的:開頭的4個位元組是標準的Unix時間戳,編碼了從新紀元開始的秒數;接下來的3個位元組存儲了機器ID;隨後則是2個位元組的進程ID;最後3個位元組存儲了進程局部的計數器,每次生成對象ID計數器都會加1。時間戳、機器ID和進程ID組合起來,提供了秒級別的唯一性。時間戳在前,意味著ObjectId大致會按照插入的順序排序。可以將其作為索引提高效率,但不是絕對的,只是大致。這4個位元組也隱含了文檔創建的時間,絕大多數驅動都會公開一個方法從ObjectId獲取這個資訊。前9個位元組保證了同一秒不同機器不同進程產生的ObjectId是唯一的,後3位元組就是一個自動增加的計數器,保證了相同進程同一秒產生的ObjectId也是不一樣的。同一秒鐘允許每個進程擁有2563(16777216)個不同的ObjectId。

當前4.2版本中是這樣介紹的,ObjectId各個段含義如下:

前4個位元組的值,表示自Unix紀元以來的秒數。中間5個位元組是隨機值。最後3個位元組是計數器,以隨機值開始。

{「x」 : objectId() }

使用ObjectId有以下兩個優點:

  • 1.在MongoDB shell中可以使用該ObjectId.getTimestamp()方法訪問創建時間。
  • 2._id存儲的ObjectId值的排序大致是按創建時間排序的。
> ObjectId("5b4c65a07a88f6e8893b70ef").getTimestamp()  ISODate("2018-07-16T09:30:08Z")

MongoDB中存儲的文檔必須有一個「_id」鍵,這個鍵可以是任何類型的,默認是ObjectId對象。在一個集合中,每個文檔都有唯一的「_id」值,來確保集合裡面每個文檔都能被唯一標識。此唯一是在一個集合中保證全局唯一的。

ObjectId是「_id」的默認類型。它設計成輕量型,不同的機器都能用全局唯一的同種方法方便地生成它。這是MongoDB採用這種類型的主要原因。

如果插入文檔的時候沒有「_id」鍵,系統會自動創建一個。這件事可以由MongoDB伺服器來做,也可以在客戶端由驅動程式完成。

通常會將自動生成_id放在客戶端讓驅動程式來完成,理由如下:

  • 1.ObjectId的生成是有開銷的,在客戶端生成可以減少資料庫擴展的負擔。
  • 2.在客戶端生成ObjectId,驅動程式能夠提供更加豐富的API。

7、Boolean(布爾)

布爾類型有兩個值true和false。

{ 「x」 : true }

8、Date(日期)

日期類型存儲的是從標準紀元開始的毫秒數,不存儲時區。

{「x」 : new Date() }

日期類型存儲的日期大概為2.9億年。毫秒數為負值,表示1970年之前的日期。

在JavaScript中,Date對象用做MongoDB的日期類型,創建一個新的Date對象時,調用new Date()而不是Date()。調用Date()實際上會返回對日期的字元串表示,而不是真正的Date對象。這不是MongoDB的特性,而是JavaScript本身的特性。

如果使用錯誤,就會導致日期和字元串混淆,字元串和日期不能互相匹配,最終會給刪除、更新、查詢等很多操作帶來問題。

shell中的日期顯示時使用本地時區設置。日期在數據中是以標準紀元開水的毫秒數的形式存儲的,沒有與之相關的時區資訊。

9、Null

null用於表示空值或者不存在的欄位。

{「x」 : null }

10、Regular Expression(正則表達式)

文檔中可以包含正則表達式,採用JavaScript的正則表達式語法。

{ 「x」 : /foobar/i }

11、JavaScript程式碼

文檔中還可以包含JavaScript程式碼。

{「x」: function() { /*…*/} }

12、Symbol(符號)

shell不支援這種類型。shell將資料庫里的符號類型轉換成字元串。現在已經過時。

13、Timestamp(時間戳)

BSON有一個MongoDB內部使用的特殊的時間戳類型,和常的日期類型沒有關係。

時間戳記值是64位值,其中:前32位是一個time_t值(自Unix時代以來的秒數),後32位是ordinal給定秒內操作的增量。

在單個mongod實例中,時間戳記值始終是唯一的。

在複製中,操作日誌具有一個ts欄位。該欄位中的值反映了使用BSON時間戳值的操作時間。

注意時間戳類型只是在MongoDB內部使用。開發過程中使用的是日期類型。

14、數字

32-bit integer(32位整數)

shell中這個類型不可用,因為JavaScript僅支援64位浮點數,所以32位整數會被自動轉換為為64位浮點數。

64-bit integer(64位整數)

shell中也不支援這個類型,shell中會使用一個特殊的內嵌文檔來顯示64位整數。

Double(64位浮點數)

JavaScript中只有一種數字類型。MongoDB中有3種數字類型,shell必須繞過JavaScript的限制。默認情況下,shell中的數字都被MongoDB當作是雙精度數。這就意味著如果從資料庫張總獲得一個32位整數,修改文檔後,將文檔存回資料庫的時候,這個整數也被轉換成了浮點數,即便是保持這個整數原封不動存回去,也是這樣的。所以盡量不要在shell下覆蓋整個文檔。

數字只能表示為雙精度數,有些64位的整數並不能精確地表示為64位浮點數。所以要是存入一個64位整數,然後在shell中查看,它會顯示一個內嵌文檔,表示可能不準確。

例如,在集合中存入一個文檔(不是在shell模式下存入的),其中myInterger鍵的值設為一個64位整數3,然後在shell中查看,如下:

>doc = db.nums.findOn();  {  	「_id」 : ObjectId(「4c0beecfd096a2580fe6fa08」),  	「myInteger」 : {  	「floatApprox」 : 3  }  }

內嵌文檔只表示shell顯示的是一個64位浮點數近似表示的64位整數,若內嵌文檔只有一個鍵的話,實際上這個值是準確的。

要是插入的64位整數不能精確地作為雙精度數顯示,shell會添加兩個鍵,分別是「top」(表示高32位)和「bottom」(表示低32位)。

例如,插入9223372036854775807,shell顯示如下:

>doc = db.nums.findOn();  {  	「_id」 : ObjectId(「4c0beecfd096a2580fe6fa10」),  	「myInteger」 : {  	「floatApprox」 : 9223372036854776000,  	「top」 :2147483647,  	「bottom」 : 4294967295  }  }

floatApprox是一種特殊的內嵌文檔,可以作為值和文檔來操作。

>doc.myInteger.floatApprox  3  > doc.myInteger + 1  4

15、Man key(最大值)

BSON包括一個特殊類型,表示可能的最大值。shell中沒有這個類型。

16、Min key(最小值)

BSON包括一個特殊類型,表示可能的最小值。shell中沒有這個類型。

二、類型之間的比較和排序

比較不同BSON類型的值時,MongoDB使用以下比較順序,從最低到最高:

MinKey(內部類型)、Null、數字(整數,整數,雙精度數,小數)、符號,字元串、Object、數組、BinData、ObjectId、布爾、日期、時間戳、正則表達式、MaxKey(內部類型)

1、數值類型

為了進行比較,MongoDB將這些類型視為等效的,在進行比較之前,先將數字類型進行轉換。

2、字元串

二進位比較法

默認情況下,MongoDB將字元串轉換成二進位來進行比較。

Collation

Collation是3.4版本的新功能,Collation允許用戶為字元串比較指定特定的語言規則。

Collation具有以下語法:

{     locale: <string>,     caseLevel: <boolean>,     caseFirst: <string>,     strength: <int>,     numericOrdering: <boolean>,     alternate: <string>,     maxVariable: <string>,     backwards: <boolean>  }

指定排序規則時,該locale欄位為必填欄位;所有其他排序規則欄位都是可選的。

locale

用來選擇語言環境,官方提供了全球很多國家的語言,在其中可以看到中文的選項值為zh,英文的值為en。其他值的選項,如下:

Locale

caseFirst

alternate

normalization

backwards

zh

off

non-ignorable

FALSE

FALSE

en

off

non-ignorable

FALSE

FALSE

默認排序規則參數值取決於語言環境。以下默認參數在所有語言環境中都是一致的:

caseLevel : false  strength : 3  numericOrdering : false  maxVariable : punct

如果沒有為集合或操作指定排序規則,則MongoDB使用先前版本中使用的簡單二進位比較進行字元串比較。

3、Arrays

對於數組,小於比較或升序排序比較的是數組中的最小元素,大於比較或降序排序比較的是數組中的最大元素。

當欄位是單元素數組與非數組欄位進行比較時,比較的是數組的元素和非數組欄位的值。空數組參與比較的話,會將空數組視為小於null或缺少此欄位。

4、Objects

MongoDB對BSON對象的比較使用以下順序:

  • 1.按照鍵值對在BSON對象中出現的順序遞歸比較它們。
  • 2.比較關鍵欄位名稱。
  • 3.如果關鍵欄位名稱相等,則比較欄位值。
  • 4.如果欄位值相等,則比較下一個鍵/值對(返回步驟1)。沒有下一個欄位的對象小於有下一個欄位的對象。

5、日期和時間戳

在3.0.0版本中進行了更改,將日期對象放在時間戳對象之前排序。

在早期的版本中是將兩種對象放在一起進行比較的。

6、不存在的欄位

MongoDB將不存在的欄位視為是空的BSON對象。

例如:{}和{a : null}進行比較,那麼在比較的時候,a欄位和空文檔將視為等價的。

7、BinData

MongoDB按BinData以下順序排序:

  • 首先,比較數據的長度或大小。
  • 然後,按BSON的一位元組子類型進行比較。
  • 最後,根據數據執行逐位元組比較。