我們通過學(xué)習(xí) MongoDB 的基本工作原理,開始我們的 MongoDB 之旅。當(dāng)然,這是學(xué)習(xí) MongoDB 的核心,它也能幫助我們回答諸如,MongoDB 適用于哪些場景這些更高層次的問題。
開始之前,這有六個簡單的概念我們需要了解一下。
MongoDB中的?database
?有著和你熟知的"數(shù)據(jù)庫"一樣的概念 (對 Oracle 來說就是 schema)。一個 MongoDB 實(shí)例中,可以有零個或多個數(shù)據(jù)庫,每個都作為一個高等容器,用于存儲數(shù)據(jù)。
數(shù)據(jù)庫中可以有零個或多個?collections
?(集合)。集合和傳統(tǒng)意義上的?table
?基本一致,你可以簡單的把兩者看成是一樣的東西。
集合是由零個或多個?documents
?(文檔)組成。同樣,一個文檔可以看成是一?row
。
文檔是由零個或多個?fields
?(字段)組成。, 沒錯,它就是?columns
。
Indexes
?(索引)在 MongoDB 中扮演著和它們在 RDBMS 中一樣的角色。
Cursors
?(游標(biāo))和上面的五個概念都不一樣,但是它非常重要,并且經(jīng)常被忽視,因此我覺得它們值得單獨(dú)討論一下。其中最重要的你要理解的一點(diǎn)是,游標(biāo)是,當(dāng)你問 MongoDB 拿數(shù)據(jù)的時候,它會給你返回一個結(jié)果集的指針而不是真正的數(shù)據(jù),這個指針我們叫它游標(biāo),我們可以拿游標(biāo)做我們想做的任何事情,比如說計數(shù)或者跨行之類的,而無需把真正的數(shù)據(jù)拖下來,在真正的數(shù)據(jù)上操作。綜上,MongoDB 是由包含?collections
?的?databases
?組成的。而?collection
?是由?documents
組成。每個?document
是由?fields
?組成。?Collections
?可以被?indexed
,以便提高查找和排序的性能。最后,當(dāng)我們從 MongoDB 獲取數(shù)據(jù)的時候,我們通過?cursor
?來操作,讀操作會被延遲到需要實(shí)際數(shù)據(jù)的時候才會執(zhí)行。
那為什么我們需要新的術(shù)語(collection vs. table, document vs. row and field vs. column)?為了讓看起來更復(fù)雜點(diǎn)?事實(shí)上,雖然這些概念和關(guān)系型數(shù)據(jù)中的概念類似,但是還是有差異的。核心差異在于,關(guān)系型數(shù)據(jù)庫是在?table
?上定義的columns
,而面向文檔數(shù)據(jù)庫是在?document
?上定義的?fields
。也就是說,在?collection
?中的每個?document
?都可以有它自己獨(dú)立的?fields
。因此,對于?collection
?來說是個簡化了的?table
?,但是一個?document
?卻比一?row
?有更多的信息。
雖然這些概念很重要,但是如果現(xiàn)在搞不明白也不要緊。多插幾條數(shù)據(jù)就明白上面說的到底是什么意思了。反正,要點(diǎn)就是,集合不對存儲內(nèi)容嚴(yán)格限制 (所謂的無模式(schema-less))。字段由每個獨(dú)立的文檔進(jìn)行跟蹤處理。這樣做的優(yōu)點(diǎn)和缺點(diǎn)將在下面章節(jié)一一討論。
好了我們開始吧。如果你還沒有運(yùn)行 MongoDB,那么快去運(yùn)行?mongod
?服務(wù)和開啟 mongo shell。shell 用的是 JavaScript。你可以試試一些全局命令,比如?help
?或者?exit
。如果要操作當(dāng)前數(shù)據(jù)庫,用?db
?,比如?db.help()
?或者db.stats()
。如果要操作指定集合,大多數(shù)情況下我們會操作集合而不是數(shù)據(jù)庫,用?db.COLLECTION_NAME
?,比如db.unicorns.help()
?或者?db.unicorns.count()
。
我們繼續(xù),輸入?db.help()
,就能拿到一個對?db
?能執(zhí)行的所有的命令的列表。
順便說一句:因?yàn)檫@是一個 JavaScript shell,如果你輸入的命令漏了?()
,你會看到這個命令的源碼,而不是執(zhí)行這個命令。我提一下,是為了避免你執(zhí)行漏了括號的命令,拿到一個以?function (...){
?開頭的返回的時候,覺得神奇不可思議。比如說,如果你輸入?db.help
?(不帶括號), 你會看到?help
?方法的內(nèi)部實(shí)現(xiàn)。
首先我們用全局的?use
?來切換數(shù)據(jù)庫,繼續(xù),輸入?use learn
。這個數(shù)據(jù)庫實(shí)際存在與否完全沒有關(guān)系。我們在里面生成集合的時候,?learn
?數(shù)據(jù)庫會自動建起來?,F(xiàn)在,我們在一個數(shù)據(jù)庫里面了,你可以開始嘗試一下數(shù)據(jù)庫命令,比如db.getCollectionNames()
。執(zhí)行之后,你會得到一個空數(shù)組 ([ ]
)。因?yàn)榧鲜菬o模式的,我們不需要特地去配置它。我們可以簡單的插入一個文檔到一個新的集合。像這樣,我們用?insert
?命令,在文檔中插入:
db.unicorns.insert({name: 'Aurora',
gender: 'f', weight: 450})
這行命令對集合?unicorns
?執(zhí)行了?insert
?命令,并傳入一個參數(shù)。MongoDB 內(nèi)部用二進(jìn)制序列化 JSON 格式,稱為 BSON。外部,也就是說我們多數(shù)情況應(yīng)該用 JSON,就像上面的參數(shù)一樣。然后我們執(zhí)行?db.getCollectionNames()
?,我們將能拿到兩個集合:?unicorns
?和?system.indexes
。在每個數(shù)據(jù)庫中都會有一個?system.indexes
?集合,用來保存我們數(shù)據(jù)的的索引信息。
你現(xiàn)在可以對用?unicorns
?執(zhí)行?find
?命令,然后返回文檔列表:
db.unicorns.find()
請注意,除你指定的字段之外,會多出一個?_id
?字段。每個文檔都會有一個唯一?_id
?字段。你可以自己生成一個,或者讓 MongoDB 幫你生成一個?ObjectId
?類型的。多數(shù)情況下,你會樂意讓 MongoDB 幫你生成的。默認(rèn)的?_id
?字段是已被索引的 - 這就說明了為什么會有?system.indexes
?集合。你可以看看?system.indexes
:
db.system.indexes.find()
你可以看到索引的名字,被索引的數(shù)據(jù)庫和集合,以及在索引中的字段。
現(xiàn)在,回到我們關(guān)于數(shù)組無模式的討論中來。往?unicorns
?插入一個完全不同的文檔,比如:
db.unicorns.insert({name: 'Leto',
gender: 'm',
home: 'Arrakeen',
worm: false})
然后,再用?find
?列出文檔。等我們理解再深入一點(diǎn)的時候,將會討論一下 MongoDB 的有趣行為。到這里,我希望你開始理解,為什么那些傳統(tǒng)的術(shù)語在這里不適用了。
除了我們介紹過的六個概念,在開始討論更深入的話題之前,MongoDB 還有一個應(yīng)該掌握的實(shí)用概念:查詢選擇器。MongoDB 的查詢選擇器就像 SQL 語句里面的?where
?一樣。因此,你會在對集合的文檔做查找,計數(shù),更新,刪除的時候用到它。選擇器是一個 JSON 對象,最簡單的是就是用?{}
?匹配所有的文檔。如果我們想找出所有母獨(dú)角獸,我們可以用?{gender:'f'}
。
開始深入學(xué)習(xí)選擇器之前,讓我們先做些準(zhǔn)備。首先,把剛才我們插入?unicorns
?集合的數(shù)據(jù)刪除,通過:db.unicorns.remove({})
。現(xiàn)在,再插入一些用來演示的數(shù)據(jù) (你不會手打吧):
db.unicorns.insert({name: 'Horny',
dob: new Date(1992,2,13,7,47),
loves: ['carrot','papaya'],
weight: 600,
gender: 'm',
vampires: 63});
db.unicorns.insert({name: 'Aurora',
dob: new Date(1991, 0, 24, 13, 0),
loves: ['carrot', 'grape'],
weight: 450,
gender: 'f',
vampires: 43});
db.unicorns.insert({name: 'Unicrom',
dob: new Date(1973, 1, 9, 22, 10),
loves: ['energon', 'redbull'],
weight: 984,
gender: 'm',
vampires: 182});
db.unicorns.insert({name: 'Roooooodles',
dob: new Date(1979, 7, 18, 18, 44),
loves: ['apple'],
weight: 575,
gender: 'm',
vampires: 99});
db.unicorns.insert({name: 'Solnara',
dob: new Date(1985, 6, 4, 2, 1),
loves:['apple', 'carrot',
'chocolate'],
weight:550,
gender:'f',
vampires:80});
db.unicorns.insert({name:'Ayna',
dob: new Date(1998, 2, 7, 8, 30),
loves: ['strawberry', 'lemon'],
weight: 733,
gender: 'f',
vampires: 40});
db.unicorns.insert({name:'Kenny',
dob: new Date(1997, 6, 1, 10, 42),
loves: ['grape', 'lemon'],
weight: 690,
gender: 'm',
vampires: 39});
db.unicorns.insert({name: 'Raleigh',
dob: new Date(2005, 4, 3, 0, 57),
loves: ['apple', 'sugar'],
weight: 421,
gender: 'm',
vampires: 2});
db.unicorns.insert({name: 'Leia',
dob: new Date(2001, 9, 8, 14, 53),
loves: ['apple', 'watermelon'],
weight: 601,
gender: 'f',
vampires: 33});
db.unicorns.insert({name: 'Pilot',
dob: new Date(1997, 2, 1, 5, 3),
loves: ['apple', 'watermelon'],
weight: 650,
gender: 'm',
vampires: 54});
db.unicorns.insert({name: 'Nimue',
dob: new Date(1999, 11, 20, 16, 15),
loves: ['grape', 'carrot'],
weight: 540,
gender: 'f'});
db.unicorns.insert({name: 'Dunx',
dob: new Date(1976, 6, 18, 18, 18),
loves: ['grape', 'watermelon'],
weight: 704,
gender: 'm',
vampires: 165});
現(xiàn)在我們有數(shù)據(jù)了,我們可以開始來學(xué)習(xí)掌握選擇器了。{field: value}
?用來查找那些?field
?的值等于?value
?的文檔。?{field1: value1, field2: value2}
?相當(dāng)于?and
?查詢。還有?$lt
,?$lte
,?$gt
,?$gte
?和?$ne
?被用來處理 小于,小于等于,大于,大于等于,和不等于操作。比如,獲取所有體重大于700磅的公獨(dú)角獸,我們可以這樣:
db.unicorns.find({gender: 'm',
weight: {$gt: 700}})
//or (not quite the same thing, but for
//demonstration purposes)
db.unicorns.find({gender: {$ne: 'f'},
weight: {$gte: 701}})
$exists
?用來匹配字段是否存在,比如:
db.unicorns.find({
vampires: {$exists: false}})
會返回一條文檔。'$in' 被用來匹配查詢文檔在我們傳入的數(shù)組參數(shù)中是否存在匹配值,比如:
db.unicorns.find({
loves: {$in:['apple','orange']}})
會返回那些喜歡?apple
?或者?orange
?的獨(dú)角獸。
如果我們想要 OR 而不是 AND 來處理選擇條件的話,我們可以用?$or
?操作符,再給它一個我們要匹配的數(shù)組:
db.unicorns.find({gender: 'f',
$or: [{loves: 'apple'},
{weight: {$lt: 500}}]})
上面的查詢會返回那些喜歡?apples
?或者?weigh
?小于500磅的母獨(dú)角獸。
在我們最后兩個例子里面有個非常贊的特性。你應(yīng)該已經(jīng)注意到了,loves
?字段是個數(shù)組。MongoDB 允許數(shù)組作為基本對象(first class objects)處理。這是個令人難以置信的超贊特性。一旦你開始用它,你都不知道沒了它你怎么活下去了。最有趣的是,基于數(shù)組的查詢變得非常簡單:?{loves: 'watermelon'}
?會把文檔中?loves
?中有?watermelon
?的值全部查詢出來。
除了我們介紹的這些,還有更多可用的操作。所有這些都記載在 MongoDB 手冊上的?Query Selectors?這一章。我們介紹的僅僅是那些你學(xué)習(xí)時所需要用到的,同時也是你最經(jīng)常用到的操作。
我們已經(jīng)學(xué)習(xí)了選擇器是如何配合?find
?命令使用的了。還大致介紹了一下如何配合?remove
?命令使用,count
?命令雖然沒介紹,不過你肯定知道應(yīng)該怎么做,而?update
?命令,之后我們會花多點(diǎn)時間來詳細(xì)學(xué)習(xí)它。
MongoDB 為我們的?_id
?字段生成的?ObjectId
?可以這樣查詢:
db.unicorns.find(
{_id: ObjectId("TheObjectId")})
我們還沒有看到?update
?, 或是能拿來做更華麗事情的?find
。不過,我們已經(jīng)安裝好 MongoDB 并運(yùn)行起來了, 簡略的介紹了一下?insert
?和?remove
?命令 (完整版也沒比我們介紹的多什么)。 我們還介紹了?find
?以及了解了 MongoDBselectors
?是怎么一回事。 我們起了個很好的頭,并為以后的學(xué)習(xí)奠定了堅實(shí)基礎(chǔ)。 信不信由你,其實(shí)你已經(jīng)掌握了學(xué)習(xí) MongoDB 所必須的大多數(shù)知識 - 它真的是易學(xué)易用。 我強(qiáng)烈建議你在繼續(xù)學(xué)習(xí)之前在本機(jī)上多試試多玩玩。 插入不同的文檔,可以試試看在不同的集合中,習(xí)慣一下使用不同的選擇器。試試?find
,?count
?和?remove
。 多試幾次之后,你會發(fā)現(xiàn)原來看起來那么格格不入的東西,用起來居然水到渠成。
更多建議: