這里是閱讀了《MongoDB
權(quán)威指南》后做的相關(guān)筆記。
一梧油、文檔
文檔是MongoDB
的核心概念苫耸。文檔就是鍵值對(duì)的一個(gè)有序集合。在JS
中儡陨,文檔被表示為對(duì)象:
{"greeting" : "Hello, world!"}
或
{"greeting" : "Hello, world!", "foo" : 3}
文檔的鍵是字符串褪子。除了少數(shù)例外情況,鍵可以使用任意UTF-8
字符骗村。
- 鍵不能含有
\0
(空字符)嫌褪。這個(gè)字符用于表示鍵的結(jié)尾。 -
.
和$
具有特殊意義胚股,只能在特定環(huán)境下使用笼痛。 -
MongoDB
不但區(qū)分類型,而且區(qū)分大小寫。下面兩個(gè)文旦是不同的:
{"foo" : 3}
{"foo" : "3"}
下面兩個(gè)文檔也是不同的:
{"foo" : 3}
{"Foo" : 3}
- 文檔不能有重復(fù)的鍵缨伊。而且文檔中的鍵值對(duì)是有序的摘刑。下面兩個(gè)文檔是不同的:
{"x" : 1, "y" : 2}
{"y" : 2, "x" : 1}
二、集合
集合就是一組文檔倘核。
2.1 動(dòng)態(tài)模式
集合是動(dòng)態(tài)的泣侮,也就是說(shuō)即彪,一個(gè)集合里的文檔可以是各式各樣的紧唱。如下面的文檔可以同時(shí)存在于同一個(gè)集合中。
{"greeting" : "Hello, world!"}
{"foo" : 5}
2.2 命名
集合使用名稱進(jìn)行標(biāo)識(shí)隶校。集合名可以是滿足下列條件的任意UTF-8
字符串漏益。
- 集合名不能是空字符串(
""
)。 - 集合名不能包含
\0
字符串(空字符)深胳,這個(gè)字符表示集合名的結(jié)束绰疤。 - 集合名不能以
"system."
開頭,這是為系統(tǒng)集合保留的前綴舞终。如轻庆,system.users
集合保存著數(shù)據(jù)庫(kù)的用戶信息,而system.namespaces
集合保存著所有數(shù)據(jù)庫(kù)集合的信息敛劝。 - 用戶創(chuàng)建的集合不能在集合名中包含保留字符
'$'
余爆。因?yàn)槟承┫到y(tǒng)生成的集合中包含它。
子集合
組織集合的一種管理是使用"."
分隔不同命名空間的子集合夸盟。如蛾方,一個(gè)具有博客功能的應(yīng)用可能包含兩個(gè)集合,分別是blog.posts上陕、blog.authors
桩砰。這是為了使組織結(jié)構(gòu)更清晰,這里的blog
集合跟其子集合沒有任何關(guān)系释簿。
三亚隅、 數(shù)據(jù)庫(kù)
在MongoDB
中,多個(gè)文檔年組成集合庶溶,而多個(gè)集合可以組成數(shù)據(jù)庫(kù)煮纵。數(shù)據(jù)庫(kù)通過名稱來(lái)標(biāo)識(shí),這點(diǎn)與集合類似渐尿。數(shù)據(jù)庫(kù)名可以是滿足以下條件的任意UTF-8
字符串醉途。
- 不能是空字符串("")
- 不得含有
/、\砖茸、.隘擎、"、*凉夯、<货葬、>采幌、:、|震桶、?休傍、$
(一個(gè)空格)、\0
(空字符)蹲姐∧ト。基本上,只能使用ASCII
中的字母和數(shù)字柴墩。 - 數(shù)據(jù)庫(kù)名區(qū)分大小寫忙厌,即便是在不區(qū)分大小寫的文件系統(tǒng)中也是如此。簡(jiǎn)單起見江咳,數(shù)據(jù)庫(kù)名應(yīng)全部小寫逢净。
- 數(shù)據(jù)庫(kù)名最多
64
字節(jié)。
注意:數(shù)據(jù)庫(kù)最終會(huì)變成文件系統(tǒng)里的文件歼指,而數(shù)據(jù)庫(kù)名就是相應(yīng)的文件名爹土。另外有一些數(shù)據(jù)庫(kù)名是保留的,可以直接訪問這些有特殊語(yǔ)義的數(shù)據(jù)庫(kù)踩身。
admin
從身份驗(yàn)證的角度來(lái)講胀茵,這是"root"
數(shù)據(jù)庫(kù)。如果將一個(gè)用戶添加到admin
數(shù)據(jù)庫(kù)惰赋,這個(gè)用戶將自動(dòng)獲得所有數(shù)據(jù)庫(kù)的權(quán)限宰掉。再者,一些特定的服務(wù)器端命令也只能從admin
數(shù)據(jù)庫(kù)運(yùn)行赁濒,如列出所有數(shù)據(jù)庫(kù)或關(guān)閉服務(wù)器轨奄。local
這個(gè)數(shù)據(jù)庫(kù)永遠(yuǎn)都不可以復(fù)制,且一臺(tái)服務(wù)器上的所有本地集合都可以存儲(chǔ)在這個(gè)數(shù)據(jù)庫(kù)中拒炎。config
MongoDB
用于分片設(shè)置時(shí)挪拟,分片信息會(huì)存儲(chǔ)在config
數(shù)據(jù)庫(kù)中。
四击你、MongoDB簡(jiǎn)介
4.1 shell 中的基本操作
4.1.1 數(shù)據(jù)庫(kù)查看
db //表示查看當(dāng)前使用的是哪個(gè)數(shù)據(jù)庫(kù)
show dbs //查看所有的數(shù)據(jù)庫(kù)
use test //更換數(shù)據(jù)庫(kù)為test
4.1.2 創(chuàng)建
說(shuō)明:這里創(chuàng)建一個(gè)名為
post
的局部變量玉组,這是一個(gè)JS
對(duì)象,用于表示文檔丁侄。注意惯雳,換行的時(shí)候不能直接使用回車,應(yīng)先打//
對(duì)回車進(jìn)行轉(zhuǎn)義鸿摇。這個(gè)對(duì)象是一個(gè)有效的MongoDB
文檔石景,使用insert
方法可以將其保存到blog
集合中。說(shuō)明:如上,將文檔保存到集合中之后潮孽,可以使用
find()
方法查看揪荣。同時(shí),可以看到曾輸入的完整文檔往史,還有一個(gè)額外添加的鍵"_id"
仗颈。其實(shí)這就和主鍵類似。
4.1.3 讀取
這里可以使用方法find
和findOne
查詢集合里的文檔椎例。若只想查看一個(gè)文檔挨决,可用findOne
。
這兩個(gè)方法可以接受一個(gè)查詢文檔作為限定條件粟矿。這樣就可以查詢符合一定條件的文檔凰棉。使用
find
時(shí)损拢,shell
會(huì)自動(dòng)顯示最多二十個(gè)匹配的文檔陌粹,也可以獲得更多文檔。
4.1.4 更新
使用update
修改博客文章福压。其接受(至少)兩個(gè)參數(shù):第一個(gè)是限定條件(用于匹配待更新的文檔)掏秩,第二個(gè)是新的文檔。現(xiàn)在如果我們要為先前寫的文章增加評(píng)論功能荆姆,就需要增加一個(gè)新的鍵蒙幻,用于保存評(píng)論數(shù)組。
說(shuō)明:這里先修改
post
變量胆筒,增加了"comments"
鍵。然后執(zhí)行update
操作,其中第一個(gè)參數(shù)表示限定條件橱脸,即匹配標(biāo)題為"My Blog Post"
的文章膳沽。
4.1.5 刪除
使用remove
方法可將文檔從數(shù)據(jù)庫(kù)中永久刪除。如果沒有使用任何參數(shù)彤蔽,它會(huì)將集合內(nèi)的所有文檔全部刪除摧莽。接受一個(gè)作為限定條件的文檔作為參數(shù)。
五顿痪、數(shù)據(jù)類型
5.1 基本數(shù)據(jù)類型
在概念上镊辕,MongoDB
的文檔與JS
中的對(duì)象相近,因而可認(rèn)為它類似于JSON
蚁袭。JSON
是一種簡(jiǎn)單的數(shù)據(jù)表示方式:其規(guī)范僅用一段文字就能描述清楚征懈,且僅包含六種數(shù)據(jù)類型。這其中只有null
揩悄、布爾卖哎、數(shù)字、字符串、數(shù)組和對(duì)象這幾種數(shù)據(jù)類型棉饶,所以JSON
的表達(dá)能力有一定的局限厦章。如JSON
沒有日期類型。只有一種數(shù)字類型照藻,無(wú)法區(qū)分浮點(diǎn)數(shù)和整數(shù)袜啃,更別說(shuō)區(qū)分32
位和64
位數(shù)字了⌒衣疲基于此原因群发,MongoDB
在保留JSON
基本鍵值對(duì)的基礎(chǔ)上,添加了其他一些數(shù)據(jù)類型发乔。
null
null
用于表示空值或者不存在的字段:
{"x" : null}
- 布爾值
布爾類型有兩個(gè)值true
和false
:
{"x" : true}
- 數(shù)值
shell
默認(rèn)使用64
位浮點(diǎn)數(shù)值熟妓。
{"x" : 3.14}
{"x" : 3}
對(duì)于整型值,可使用NumberInt
類(表示四字節(jié)帶符號(hào)整數(shù))或NumberLong
類(表示八字節(jié)帶符號(hào)整數(shù))栏尚,分別舉例如下:
{"x" : NumberInt("3")}
{"x" : NumberLong("3")}
- 字符串
UTF-8
字符串都可表示為字符串類型的數(shù)據(jù)
{"x" : "foobar"}
- 日期
日期被存儲(chǔ)為自新紀(jì)元來(lái)經(jīng)過的毫秒數(shù)起愈,不存儲(chǔ)時(shí)區(qū):
{"x" : new Date()}
- 正則表達(dá)式
查詢時(shí),使用正則表達(dá)式作為限定條件译仗,語(yǔ)法也與JS
的正則表達(dá)式語(yǔ)法相同:
{"x" : /foobar/i}
- 數(shù)組
數(shù)據(jù)列表或數(shù)據(jù)集可以表示為數(shù)組
{"x" : ["a", "b", "c"]}
- 內(nèi)嵌文檔
文檔可嵌套其他文檔抬虽,被嵌套的文檔作為父文檔的值
{"x" : {"foo" : "bar"}}
- 對(duì)象
id
對(duì)象id
是一個(gè)12
字節(jié)的ID
,是文檔的唯一標(biāo)識(shí)纵菌。
{"x" : ObjectId()}
還有一些不那么常用阐污,但可能有需要的類型,包括下面這些咱圆。
二進(jìn)制數(shù)據(jù)
二進(jìn)制數(shù)據(jù)是一個(gè)任意字節(jié)的字符串笛辟。它不能直接在shell
中使用。如果要將非UTF-8
字符保存到數(shù)據(jù)庫(kù)中序苏,二進(jìn)制數(shù)據(jù)是唯一的方式手幢。代碼
查詢和文檔中可以包括任意JS
代碼
{"x" : function(){/*...*/}}
5.2 日期
在JS
中,Date
類型可以用作MongoDB
的日期類型杠览。創(chuàng)建日期對(duì)象時(shí)弯菊,應(yīng)使用new Date(...)
,而非Date(...)
踱阿。如果將構(gòu)造函數(shù)作為函數(shù)進(jìn)行調(diào)用(即不包括new
的方式)管钳,返回的是日期的字符串表示,而非日期(Date
)對(duì)象软舌。shell
根據(jù)本地時(shí)區(qū)設(shè)置顯示日期對(duì)象才漆。然而,數(shù)據(jù)庫(kù)中存儲(chǔ)的日期僅為新紀(jì)元以來(lái)的毫秒數(shù)佛点,并未存儲(chǔ)對(duì)應(yīng)的時(shí)區(qū)醇滥。(當(dāng)然黎比,可將時(shí)區(qū)信息存儲(chǔ)為另一個(gè)鍵的值)。
5.3 數(shù)組
數(shù)組是一組值鸳玩,它既能作為有序?qū)ο螅ㄈ缌斜碓某妗;蜿?duì)列)不跟,也能作為無(wú)無(wú)序?qū)ο螅ㄈ鐢?shù)據(jù)集)來(lái)操作颓帝。在下面的文檔中,"things"
這個(gè)鍵的值是一個(gè)數(shù)組:
{"things" : ["pie", 3.14]}
此例表示窝革,數(shù)組可包含不同數(shù)據(jù)類型的元素(在此购城,是一個(gè)字符串和一個(gè)浮點(diǎn)數(shù))。實(shí)際上虐译,常規(guī)的鍵值對(duì)支持的所有值都可以作為數(shù)組的值瘪板,數(shù)組中甚至可以嵌套數(shù)組。
文檔中的數(shù)組有個(gè)奇妙的特性漆诽,就是MongoDB
能理解其結(jié)構(gòu)侮攀,并知道如何深入數(shù)組內(nèi)部對(duì)其內(nèi)容進(jìn)行操作。這樣就能使用數(shù)組內(nèi)容對(duì)數(shù)組進(jìn)行查詢和構(gòu)建索引了拴泌。如之前的例子中魏身,可以查詢出"things"
數(shù)組中包含3.14
這個(gè)元素的所有文檔。還可以使用原子更新對(duì)數(shù)組內(nèi)容進(jìn)行修改蚪腐,這在后面講解。
5.4 內(nèi)嵌文檔
文檔可以作為鍵的值税朴,這樣的文檔就是內(nèi)嵌文檔回季。如用一個(gè)文檔來(lái)表示一個(gè)人,同時(shí)還要保存他的地址正林,可以將地址信息保存在內(nèi)嵌的“address”
文檔中:
{
"name" : "Tom",
"address" : {
"street" : "123 Park Street",
"city" : "Anytown",
"state" : "NY"
}
}
同數(shù)組一樣泡一,MongoDB
能夠理解內(nèi)嵌文檔的結(jié)構(gòu), 并能深入其中構(gòu)建索引觅廓,執(zhí)行查詢或更新鼻忠。在關(guān)系型數(shù)據(jù)庫(kù)中,這個(gè)例子中的文檔一般會(huì)被拆分成兩個(gè)表中的兩個(gè)行("people"
和"address"
各一行)杈绸。而MongoDB
中直接將地址文檔嵌入到人員文檔中帖蔓。這樣做的壞處就是會(huì)導(dǎo)致更多的數(shù)據(jù)重復(fù)。假設(shè)"address"
是關(guān)系數(shù)據(jù)庫(kù)中的一個(gè)獨(dú)立的表瞳脓,我們需要修正地址中的錯(cuò)誤塑娇。當(dāng)我們對(duì)"people"
和"address"
執(zhí)行連續(xù)操作時(shí),使用這個(gè)地址的每個(gè)人的信息都會(huì)得到更新劫侧。但是在MongoDB
中埋酬,則需要對(duì)每個(gè)人的文檔分別修正拼寫錯(cuò)誤哨啃。
5.5 _id 和 ObjectId
在MongoDB
中存儲(chǔ)的文檔必須有一個(gè)"_id"
鍵。這個(gè)鍵的值可以是任何類型的写妥,默認(rèn)是個(gè)ObjectId
對(duì)象拳球。在一個(gè)集合里面,每個(gè)文檔都有唯一的"_id"
珍特,確保集合里面每個(gè)文檔都能被唯一標(biāo)識(shí)醇坝。如果有兩個(gè)集合的話,兩個(gè)集合可以都有一個(gè)"_id"
的值為123
(一個(gè)數(shù)據(jù)庫(kù)中集合名不能重復(fù))次坡,但是每個(gè)集合里面只能有一個(gè)文檔的"_id"
值為123
呼猪。
5.5.1 ObjectId
ObjectId
是"_id"
的默認(rèn)類型。它設(shè)計(jì)成輕量型的砸琅,不同的機(jī)器都能用全局唯一的同一種方法方便的生成它宋距。這是MongoDB
不采用其他比較常規(guī)的做法(比如自動(dòng)增加的主鍵)的主要原因,因?yàn)樵诙鄠€(gè)服務(wù)器上同步自動(dòng)增加主鍵既費(fèi)時(shí)又費(fèi)力症脂。
ObjectId
使用十二字節(jié)的存儲(chǔ)空間谚赎,是一個(gè)由二十四個(gè)十六進(jìn)制數(shù)字組成的字符串(每個(gè)字節(jié)可以存儲(chǔ)兩個(gè)十六進(jìn)制數(shù)字)。如果快速連續(xù)創(chuàng)建多個(gè)ObjectId
诱篷,會(huì)發(fā)現(xiàn)每次只有最護(hù)幾位數(shù)字有變化壶唤。另外,中間的幾位數(shù)字也會(huì)變化(要是在創(chuàng)建的過程中停頓幾秒)棕所。這是ObjectId
的創(chuàng)建方式導(dǎo)致的闸盔。其十二字節(jié)按照如下方式生成:
說(shuō)明:
ObjectId
的前四個(gè)字節(jié)是從標(biāo)準(zhǔn)紀(jì)元開始的時(shí)間戳,單位為秒琳省。這樣會(huì)帶來(lái)一些有用的屬性迎吵。
- 時(shí)間戳,與隨后的五字節(jié)組合起來(lái)针贬,提供了秒級(jí)別的唯一性击费。
- 由于時(shí)間戳在前,這意味著
ObjectId
大致會(huì)按照插入的順序排列桦他。但是不是絕對(duì)的蔫巩。 - 這四字節(jié)也隱含了文檔創(chuàng)建的事件。絕大多數(shù)驅(qū)動(dòng)程序都會(huì)提供一個(gè)方法快压,用于從
ObjectId
獲取這些信息圆仔。
用戶不必?fù)?dān)心多服務(wù)器時(shí)鐘同步的問題,因?yàn)闀r(shí)間戳的實(shí)際值并不重要嗓节,只要它總是不停增加就好了(每秒一次)荧缘。接下來(lái)的三字節(jié)是所在主機(jī)的唯一標(biāo)識(shí)。通常是機(jī)器主機(jī)名的散列值拦宣。這樣就可以確保不同主機(jī)生成不同的ObjectId
截粗,不產(chǎn)生沖突信姓。為了確保在同一臺(tái)機(jī)器上并發(fā)的多個(gè)進(jìn)程產(chǎn)生的ObjectId
是唯一的,接下來(lái)的兩字節(jié)來(lái)自產(chǎn)生ObjectId
的進(jìn)程的進(jìn)程標(biāo)識(shí)符(PID
)绸罗。
前九字節(jié)保證了同一秒鐘不同機(jī)器不同進(jìn)程產(chǎn)生的ObjectId
是唯一的意推。最后三字節(jié)是一個(gè)自動(dòng)增加的計(jì)數(shù)器,確保相同進(jìn)程同一秒產(chǎn)生的ObjectId
也是不一樣的珊蟀。一秒鐘最多允許每個(gè)進(jìn)程擁有2563
個(gè)不同的ObjectId
菊值。
5.5.2 自動(dòng)生成 _id
在創(chuàng)建文檔的時(shí)候,如果文檔沒有插入"_id"
鍵育灸,系統(tǒng)會(huì)自動(dòng)創(chuàng)建一個(gè)腻窒。
六、使用 MongoDB shell
在之前已經(jīng)講過磅崭,可以使用命令mongo 127.0.0.1:12345/test
連接相關(guān)數(shù)據(jù)庫(kù)儿子。而同時(shí)我們啟動(dòng)MongoDB
的時(shí)候不連接到任何MongoDB
有時(shí)也很方便。通過--nodb
參數(shù)啟動(dòng)shell
砸喻,啟動(dòng)時(shí)就不會(huì)連接到任何數(shù)據(jù)庫(kù):mongo --nodb
柔逼。啟動(dòng)之后,在需要時(shí)運(yùn)行new Mongo(hostname)
命令就可以連接到想要的MongoDB
了:
> conn = new Mongo("some-host:12345")
connection to some-host:12345
> db = conn.getDB("test")
test
6.1 shell 小貼士
可以使用help
命令查看相關(guān)幫助割岛,可以通過db.help()
查看數(shù)據(jù)庫(kù)級(jí)別的幫助愉适,使用db.test.help()
查看集合級(jí)別的幫助。如果想知道一個(gè)函數(shù)是做什么用的癣漆,可以直接在shell
輸入函數(shù)名(函數(shù)名后面不要加小括號(hào)即可)维咸,這樣就可以看到相應(yīng)函數(shù)的JS
實(shí)現(xiàn)代碼。
> db.test.update
6.2 使用shell 執(zhí)行腳本
我們?cè)?code>MongoDB中可以直接執(zhí)行相關(guān)的JS
腳本扑媚,比如現(xiàn)在給出一個(gè)腳本script1.js
(這里只是給出了一個(gè)腳本文件腰湾,其實(shí)可以同時(shí)給出多個(gè)腳本文件)。其內(nèi)容如下:
print("Hello World");
執(zhí)行時(shí)疆股,首先啟動(dòng)MongoDB
,然后使用命令
sudo bin/mongo 127.0.0.1:27017/test script1.js
執(zhí)行倒槐。
說(shuō)明:這里要注意腳本文件所在的目錄(使用正確的路徑)旬痹,而且這里還沒有連接服務(wù)器。這里使用
print()
函數(shù)將內(nèi)容輸出到標(biāo)準(zhǔn)輸出讨越,而且可以加入--quiet
參數(shù)(sudo bin/mongo --quiet 127.0.0.1:27017/test script1.js
)可以不打印MongoDB shell version...
之類的信息两残。當(dāng)然,在連接服務(wù)器之后也可以執(zhí)行腳本文件把跨。如下:在腳本中可以訪問db
變量人弓,以及其他全局變量。然而着逐,shell
輔助函數(shù)(如"use db"
)不可以在文件中使用崔赌。這些輔助函數(shù)都有對(duì)應(yīng)的JS
函數(shù)意蛀,如表所示:
輔助函數(shù) | 等價(jià)函數(shù) |
---|---|
use foo |
db.getSisterDB("foo") |
show dbs |
db.getMongo().getDBs() |
show collections |
db.getCollectionNames() |
可以使用腳本將變量注入到shell
。例如健芭,下面的腳本對(duì)于本書的復(fù)制和分片部分內(nèi)容非常有用县钥。這個(gè)腳本定義了一個(gè)connectTo()
函數(shù),它連接到指定端口處的一個(gè)本地?cái)?shù)據(jù)庫(kù)慈迈,并且將db
指向這個(gè)連接若贮。
defineConnectTo.js
var connectTo = function(port, dbname){
if(!port){
port = 27017;
}
if(!dbname){
dbname = "test";
}
db = connect("localhost:" + port + "/" + dbname);
return db;
};
如果在shell
中加載這個(gè)腳本,connectTo
函數(shù)就可以使用了痒留。
除了添加輔助函數(shù)谴麦,還可以使用腳本將通用的任務(wù)和管理活動(dòng)自動(dòng)化。默認(rèn)情況下伸头,shell
會(huì)在運(yùn)行shell
時(shí)所處的目錄中查找腳本(可以使用run("pwd")
命令查看)匾效。如果腳本不在當(dāng)前目錄中,可以為shell
指定一個(gè)相對(duì)路徑或者絕對(duì)路徑熊锭。如可以使用load("/usr/yj-software/my-mongodb/defineConnectTo.js")
來(lái)加載defineConnectTo.js
弧轧。注意,load
函數(shù)無(wú)法解析~
符號(hào)碗殷。也可以在shell中使用run()函數(shù)來(lái)執(zhí)行命令行程序精绎。可以在函數(shù)參數(shù)列表中指定程序所需的參數(shù):
通常來(lái)說(shuō)锌妻,這種使用方式的局限性很大代乃,因?yàn)檩敵龈袷胶芷婀郑也恢С止艿馈?p>