第一章 - 基礎(chǔ)知識

2018-02-24 16:17 更新

我們通過學(xué)習(xí) MongoDB 的基本工作原理,開始我們的 MongoDB 之旅。當(dāng)然,這是學(xué)習(xí) MongoDB 的核心,它也能幫助我們回答諸如,MongoDB 適用于哪些場景這些更高層次的問題。

開始之前,這有六個簡單的概念我們需要了解一下。

  1. MongoDB中的?database?有著和你熟知的"數(shù)據(jù)庫"一樣的概念 (對 Oracle 來說就是 schema)。一個 MongoDB 實(shí)例中,可以有零個或多個數(shù)據(jù)庫,每個都作為一個高等容器,用于存儲數(shù)據(jù)。

  2. 數(shù)據(jù)庫中可以有零個或多個?collections?(集合)。集合和傳統(tǒng)意義上的?table?基本一致,你可以簡單的把兩者看成是一樣的東西。

  3. 集合是由零個或多個?documents?(文檔)組成。同樣,一個文檔可以看成是一?row。

  4. 文檔是由零個或多個?fields?(字段)組成。, 沒錯,它就是?columns。

  5. Indexes?(索引)在 MongoDB 中扮演著和它們在 RDBMS 中一樣的角色。

  6. 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ù)語在這里不適用了。

掌握選擇器(Selector)

除了我們介紹過的六個概念,在開始討論更深入的話題之前,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")})

小結(jié)

我們還沒有看到?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)原來看起來那么格格不入的東西,用起來居然水到渠成。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號