上一篇文章: MongoDB指南---二、MongoDB基礎知識-文檔、集合、數據庫、客戶端
下一篇文章: MongoDB指南---四、MongoDB基礎知識-使用MongoDB Shell
本章開始部分介紹了文檔的基本概念,如今你已經會啓動、運行MongoDB,也會在shell中進行一些操做了。這一節的內容會更加深刻。MongoDB支持將多種數據類型做爲文檔中的值,下面將一一介紹。正則表達式
在概念上,MongoDB的文檔與JavaScript中的對象相近,於是可認爲它相似於JSON。JSON(http://www.json.org)是一種簡單的數據表示方式:其規範僅用一段文字就能描述清楚(其官網證實了這點),且僅包含6種數據類型。這樣有不少好處:易於理解、易於解析、易於記憶。然而,從另外一方面來講,由於只有null、布爾、數字、字符串、數組和對象這幾種數據類型,因此JSON的表達能力有必定的侷限。
雖然JSON具有的這些類型已具備很強的表現力,但絕大多數應用(尤爲是在與數據庫打交道時)都還須要其餘一些重要的類型。例如,JSON沒有日期類型,這使本來容易的日期處理變得煩人。另外,JSON只有一種數字類型,沒法區分浮點數和整數,更別說區分32位和64位數字了。再者,JSON沒法表示其餘一些通用類型,如正則表達式或函數。
MongoDB在保留JSON基本鍵/值對特性的基礎上,添加了其餘一些數據類型。 在不一樣的編程語言下,這些類型的確切表示有些許差別。下面說明MongoDB支持的其餘通用類型,以及如何在文檔中使用它們。shell
null用於表示空值或者不存在的字段:數據庫
{"x" : null}
布爾類型有兩個值true和false:編程
{"x" : true}
shell默認使用64位浮點型數值。所以,如下數值在shell中是很「正常」的:json
{"x" : 3.14}
或:segmentfault
{"x" : 3}
對於整型值,可以使用NumberInt類(表示4字節帶符號整數)或NumberLong類(表示8字符帶符號整數),分別舉例以下:數組
{"x" : NumberInt("3")} {"x" : NumberLong("3")}
UTF-8字符串均可表示爲字符串類型的數據:服務器
{"x" : "foobar"}
日期被存儲爲自新紀元以來通過的毫秒數,不存儲時區:併發
{"x" : new Date()}
查詢時,使用正則表達式做爲限定條件,語法也與JavaScript的正則表達式語法相同:ecmascript
{"x" : /foobar/i}
數據列表或數據集能夠表示爲數組:
{"x" : ["a", "b", "c"]}
文檔可嵌套其餘文檔,被嵌套的文檔做爲父文檔的值:
{"x" : {"foo" : "bar"}}
對象id是一個12字節的ID,是文檔的惟一標識。詳見2.6.5節。
{"x" : ObjectId()}
還有一些不那麼經常使用,但可能有須要的類型,包括下面這些。
二進制數據是一個任意字節的字符串。它不能直接在shell中使用。若是要將非UTF-8字符保存到數據庫中,二進制數據是惟一的方式。
查詢和文檔中能夠包括任意JavaScript代碼:
{"x" : function() { /* ... */ }}
另外,有幾種大多數狀況下僅在內部使用(或被其餘類型取代)的類型。在本書中,出現這種狀況時會特別說明。
關於MongoDB數據格式的更多信息,參考附錄B。
在JavaScript中,Date類能夠用做MongoDB的日期類型。建立日期對象時,應使用new Date(…),而非Date(…)。如將構造函數(constructor)做爲函數進行調用(即不包括new的方式),返回的是日期的字符串表示,而非日期(Date)對象。這個結果與MongoDB無關,是JavaScript的工做機制決定的。若是不注意這一點,沒有始終使用日期(Date)構造函數,將獲得一堆混亂的日期對象和日期的字符串。因爲日期和字符串之間沒法匹配,因此執行刪除、更新及查詢等幾乎全部操做時會致使不少問題。
關於JavaScript日期類的完整解釋,以及構造函數的參數格式,參見ECMAScript規範15.9節(http://www.ecmascript.org)。
shell根據本地時區設置顯示日期對象。然而,數據庫中存儲的日期僅爲新紀元以來的毫秒數,並未存儲對應的時區。(固然,可將時區信息存儲爲另外一個鍵的值)。
數組是一組值,它既能做爲有序對象(如列表、棧或隊列),也能做爲無序對象(如數據集)來操做。
在下面的文檔中,"things"這個鍵的值是一個數組:
{"things" : ["pie", 3.14]}
此例表示,數組可包含不一樣數據類型的元素(在此,是一個字符串和一個浮點數)。實際上,常規的鍵/值對支持的全部值均可以做爲數組的值,數組中甚至能夠套嵌數組。
文檔中的數組有個奇妙的特性,就是MongoDB能「理解」其結構,並知道如何「深刻」數組內部對其內容進行操做。這樣就能使用數組內容對數組進行查詢和構建索引了。例如,以前的例子中,MongoDB能夠查詢出"things"數組中包含3.14這個元素的全部文檔。要是常用這個查詢,能夠對"things"建立索引來提升性能。
MongoDB可使用原子更新對數組內容進行修改,好比深刻數組內部將pie改成pi。本書後面還會介紹更多這種操做的例子。
文檔能夠做爲鍵的值,這樣的文檔就是內嵌文檔。使用內嵌文檔,可使數據組織方式更加天然,不用非得存成扁平結構的鍵/值對。
例如,用一個文檔來表示一我的,同時還要保存他的地址,能夠將地址信息保存在內嵌的"address"文檔中:
{ "name" : "John Doe", "address" : { "street" : "123 Park Street", "city" : "Anytown", "state" : "NY" } }
上面例子中"address"鍵的值是一個內嵌文檔,這個文檔有本身的"street"、"city"和"state"鍵以及對應的值。
同數組同樣,MongoDB可以「理解」內嵌文檔的結構,並能「深刻」其中構建索引、執行查詢或者更新。
稍後會深刻討論模式設計,可是從這個簡單的例子也能夠看得出內嵌文檔能夠改變處理數據的方式。在關係型數據庫中,這個例子中的文檔通常會被拆分紅兩個表中的兩個行 (「people」和「address」各一行)。在MongoDB中,就能夠直接將地址文檔嵌入到人員文檔中。使用得當的話,內嵌文檔會使信息的表示方式更加天然(一般也會更高效)。
MongoDB這樣作的壞處就是會致使更多的數據重複。假設「address」是關係數據庫中的一個獨立的表,咱們須要修正地址中的拼寫錯誤。當咱們對「people」和「address」執行鏈接操做時,使用這個地址的每一個人的信息都會獲得更新。可是在MongoDB中,則須要對每一個人的文檔分別修正拼寫錯誤。
MongoDB中存儲的文檔必須有一個"_id"鍵。這個鍵的值能夠是任何類型的,默認是個ObjectId對象。在一個集合裏面,每一個文檔都有惟一的"_id",確保集合裏面每一個文檔都能被惟一標識。若是有兩個集合的話,兩個集合能夠都有一個"_id"的值爲123,可是每一個集合裏面只能有一個文檔的"_id"值爲123。
ObjectId是"_id"的默認類型。它設計成輕量型的,不一樣的機器都能用全局惟一的同種方法方便地生成它。這是 MongoDB採用ObjectId,而不是其餘比較常規的作法(好比自動增長的主鍵)的主要緣由,由於在多個服務器上同步自動增長主鍵值既費力又費時。由於設計MongoDB的初衷就是用做分佈式數據庫,因此可以在分片環境中生成惟一的標示符很是重要。
ObjectId使用12字節的存儲空間,是一個由24個十六進制數字組成的字符串(每一個字節能夠存儲兩個十六進制數字)。因爲看起來很長,很多人會以爲難以處理。但關鍵是要知道這個長長的ObjectId是實際存儲數據的兩倍長。
若是快速連續建立多個ObjectId,會發現每次只有最後幾位數字有變化。另外,中間的幾位數字也會變化(要是在建立的過程當中停頓幾秒鐘)。這是ObjectId的建立方式致使的。ObjectId的12字節按照以下方式生成:
ObjectId的前4個字節是從標準紀元開始的時間戳,單位爲秒。這會帶來一些有用的屬性。
由於使用的是當前時間,不少用戶擔憂要對服務器進行時鐘同步。雖然在某些狀況下,在服務器間進行時間同步確實是個好主意(參見23.6.1節),可是這裏其實沒有必要,由於時間戳的實際值並不重要,只要它老是不停增長就行了(每秒一次)。
接下來的3字節是所在主機的惟一標識符。一般是機器主機名的散列值(hash)。這樣就能夠確保不一樣主機生成不一樣的 ObjectId,不產生衝突。
爲了確保在同一臺機器上併發的多個進程產生的ObjectId是惟一的,接下來的兩字節來自產生ObjectId的進程的進程標識符(PID)。
前9字節保證了同一秒鐘不一樣機器不一樣進程產生的ObjectId是惟一的。最後3字節是一個自動增長的計數器,確保相同進程同一秒產生的ObjectId也是不同的。一秒鐘最多容許每一個進程擁有
16 777 216個不一樣的ObjectId。
前面講到,若是插入文檔時沒有"_id"鍵,系統會自動幫你建立一個。能夠由MongoDB服務器來作這件事,但一般會在客戶端由驅動程序完成。這一作法很是好地體現了MongoDB的哲學:能交給客戶端驅動程序來作的事情就不要交給服務器來作。這種理念背後的緣由是,即使是像MongoDB這樣擴展性很是好的數據庫,擴展應用層也要比擴展數據庫層容易得多。將工做交由客戶端來處理,就減輕了數據庫擴展的負擔。
上一篇文章: MongoDB指南---二、MongoDB基礎知識-文檔、集合、數據庫、客戶端
下一篇文章: MongoDB指南---四、MongoDB基礎知識-使用MongoDB Shell