上一篇文章: MongoDB指南---一、MongoDB簡介
下一篇文章: MongoDB指南---三、MongoDB基礎知識-數據類型
MongoDB很是強大但很容易上手。本章會介紹一些MongoDB的基本概念。
文檔是MongoDB中數據的基本單元,很是相似於關係型數據庫管理系統中的行,但更具表現力。
相似地,集合(collection)能夠看做是一個擁有動態模式(dynamic schema)的表。
MongoDB的一個實例能夠擁有多個相互獨立的數據庫(database),每個數據庫都擁有本身的集合。
每個文檔都有一個特殊的鍵"_id", 這個鍵在文檔所屬的集合中是惟一的。
MongoDB自帶了一個簡單但功能強大的JavaScript shell,可用於管理MongoDB的實例或數據操做。git
文檔是MongoDB的核心概念。文檔就是鍵值對的一個有序集。每種編程語言表示文檔的方法不太同樣,但大多數編程語言都有一些相通的數據結構,好比映射(map)、散列(hash)或字典(dictionary)。例如,在JavaScript 裏面,文檔被表示爲對象:web
{"greeting" : "Hello, world!"}
這個文檔只有一個鍵"greeting",其對應的值爲"Hello,world!"。大多數文檔會比這個簡單的例子複雜得多,一般會包含多個鍵/值對:mongodb
{"greeting" : "Hello, world!", "foo" : 3}
從上面的例子能夠看出,文檔中的值能夠是多種不一樣的數據類型(甚至能夠是一個完整的內嵌文檔,詳見2.6.4節)。在這個例子中,"greeting"的值是一個字符串,而"foo"的值是一個整數。
文檔的鍵是字符串。除了少數例外狀況,鍵可使用任意UTF-8字符。
鍵不能含有0(空字符)。這個字符用於表示鍵的結尾。
.和$具備特殊意義,只能在特定環境下使用(後面的章節會詳細說明)。一般,這兩個字符是被保留的;若是使用不當的話,驅動程序會有提示。
MongoDB不但區分類型,並且區分大小寫。例如,下面的兩個文檔是不一樣的:shell
{"foo" : 3} {"foo" : "3"}
下面兩個文檔也是不一樣的:數據庫
{"foo" : 3} {"Foo" : 3}
還有一個很是重要的事項須要注意,MongoDB的文檔不能有重複的鍵。例如,下面的文檔是非法的:編程
{"greeting" : "Hello, world!", "greeting" : "Hello, MongoDB!"}
文檔中的鍵/值對是有序的:{"x" : 1, "y":2}與{"y": 2, "x": 1}是不一樣的。一般,字段順序並不重要,無須讓數據庫模式依賴特定的字段順序(MongoDB會對字段從新排序)。在某些特殊狀況下,字段順序變得很是重要,本書將就此給出提示。
一些編程語言對文檔的默認表示根本就不包含順序問題(如:Python中的字典、Perl和Ruby 1.8中的散列)。一般,這些語言的驅動具備某些特殊的機制,能夠在必要時指定文檔的順序。segmentfault
集合就是一組文檔。若是將MongoDB中的一個文檔比喻爲關係型數據庫中的一行,那麼一個集合就至關於一張表。數組
集合是動態模式的。這意味着一個集合裏面的文檔能夠是各式各樣的。例如,下面兩個文檔能夠存儲在同一個集合裏面:瀏覽器
{"greeting" : "Hello, world!"} {"foo" : 5}
須要注意的是,上面的文檔不光值的類型不一樣(一個是字符串,一個是整數),它們的鍵也徹底不一樣。由於集合裏面能夠放置任何文檔,隨之而來的一個問題是:還有必要使用多個集合嗎?這的確值得思考:既然沒有必要區分不一樣類型文檔的模式,爲何還要使用多個集合呢?這裏有幾個重要的緣由。
若是把各類各樣的文檔不加區分地放在同一個集合裏,不管對開發者仍是對管理員來講都將是噩夢。開發者要麼確保每次查詢只返回特定類型的文檔,要麼讓執行查詢的應用程序來處理全部不一樣類型的文檔。若是查詢博客文章時還要剔除含有做者數據的文檔,這會帶來很大困擾。
在一個集合裏查詢特定類型的文檔在速度上也很不划算,分開查詢多個集合要快得多。例如,假設集合裏面一個名爲"type"的字段用於指明文檔是skim、whole仍是chunky monkey。那麼,若是從一個集合中查詢這三種類型的文檔,速度會很慢。但若是將這三種不一樣類型的文檔拆分爲三個不一樣的集合,每次只須要查詢相應的集合,速度快得多。
把同種類型的文檔放在一個集合裏,數據會更加集中。從一個只包含博客文章的集合裏查詢幾篇文章,或者從同時包含文章數據和做者數據的集合裏查出幾篇文章,相比之下,前者須要的磁盤尋道操做更少。
建立索引時,須要使用文檔的附加結構(特別是建立惟一索引時)。索引是按照集合來定義的。在一個集合中只放入一種類型的文檔,能夠更有效地對集合進行索引。
上面這些重要緣由促使咱們建立一個模式,把相關類型的文檔組織在一塊兒,儘管MongoDB對此並無強制要求。服務器
集合使用名稱進行標識。集合名能夠是知足下列條件的任意UTF-8字符串。
組織集合的一種慣例是使用「.」分隔不一樣命名空間的子集合。例如,一個具備博客功能的應用可能包含兩個集合,分別是blog.posts和blog.authors。這是爲了使組織結構更清晰,這裏的blog集合(這個集合甚至不須要存在)跟它的子集合沒有任何關係。
雖然子集合沒有任何特別的屬性,但它們卻很是有用,於是不少MongoDB工具都使用了子集合。
在MongoDB中,使用子集合來組織數據很是高效,值得推薦。
在MongoDB中,多個文檔組成集合,而多個集合能夠組成數據庫。一個MongoDB實例能夠承載多個數據庫,每一個數據庫擁有0個或者多個集合。每一個數據庫都有獨立的權限,即使是在磁盤上,不一樣的數據庫也放置在不一樣的文件中。按照經驗,咱們將有關一個應用程序的全部數據都存儲在同一個數據庫中。要想在同一個MongoDB服務器上存放多個應用程序或者用戶的數據,就須要使用不一樣的數據庫。
數據庫經過名稱來標識,這點與集合相似。數據庫名能夠是知足如下條件的任意UTF-8字符串。
要記住一點,數據庫最終會變成文件系統裏的文件,而數據庫名就是相應的文件名,這是數據庫名有如此多限制的緣由。
另外,有一些數據庫名是保留的,能夠直接訪問這些有特殊語義的數據庫。這些數據庫以下所示。
從身份驗證的角度來說,這是「root」數據庫。若是將一個用戶添加到admin數據庫,這個用戶將自動得到全部數據庫的權限。再者,一些特定的服務器端命令也只能從admin數據庫運行,如列出全部數據庫或關閉服務器。
這個數據庫永遠都不能夠複製,且一臺服務器上的全部本地集合均可以存儲在這個數據庫中。(第9章會詳細介紹複製及本地數據庫。)
MongoDB用於分片設置時(參見第13章),分片信息會存儲在config數據庫中。
把數據庫名添加到集合名前,獲得集合的徹底限定名,即命名空間(namespace)。例如,若是要使用cms數據庫中的blog.posts集合,這個集合的命名空間就是cms.blog.posts。命名空間的長度不得超過121字節,且在實際使用中應小於100字節。(參考附錄B,瞭解MongoDB中集合的命名空間及內部表示的更多信息。)
一般,MongoDB做爲網絡服務器來運行,客戶端可鏈接到該服務器並執行操做。下載MongoDB(http://www.mongodb.org/downloads)並解壓,運行mongod命令,啓動數據庫服務器:
$ mongod mongod --help for help and startup options Thu Oct 11 12:36:48 [initandlisten] MongoDB starting : pid=2425 port=27017 dbpath=/data/db/ 64-bit host=spock Thu Oct 11 12:36:48 [initandlisten] db version v2.4.0, pdfile version 4.5 Thu Oct 11 12:36:48 [initandlisten] git version: 3aaea5262d761e0bb6bfef5351cfbfca7af06ec2 Thu Oct 11 12:36:48 [initandlisten] build info: Darwin spock 11.2.0 Darwin Kernel Version 11.2.0: Tue Aug 9 20:54:00 PDT 2011; root:xnu-1699.24.8~1/RELEASE_X86_64 x86_64 BOOST_LIB_VERSION=1_48 Thu Oct 11 12:36:48 [initandlisten] options: {} Thu Oct 11 12:36:48 [initandlisten] journal dir=/data/db/journal Thu Oct 11 12:36:48 [initandlisten] recover : no journal files present, no recovery needed Thu Oct 11 12:36:48 [websvr] admin web console waiting for connections on port 28017 Thu Oct 11 12:36:48 [initandlisten] waiting for connections on port 27017
在Windows系統中,執行這個命令:
$ mongod.exe
mongod在沒有參數的狀況下會使用默認數據目錄/data/db(Windows系統中爲C:datadb)。若是數據目錄不存在或者不可寫,服務器會啓動失敗。所以,在啓動MongoDB前,先建立數據目錄(如mkdir -p /data/db/),以確保對該目錄有寫權限,這點很是重要。
sudo mkdir -p /data/db/ sudo chmod -R 777 /data/db
啓動時,服務器會打印版本和系統信息,而後等待鏈接。默認狀況下,MongoDB監聽27017端口。若是端口被佔用,啓動將失敗。一般,這是因爲已經有一個MongoDB實例在運行了。
mongod還會啓動一個很是基本的HTTP服務器,監聽數字比主端口號高1000的端口,也就是28017端口。這意味着,經過瀏覽器訪問http://localhost:28017,能獲取數據庫的管理信息。
停止mongod的運行,只須在運行着服務器的shell中按下Ctrl-C。
MongoDB自帶JavaScript shell,可在shell中使用命令行與MongoDB實例交互。shell很是有用,經過它能夠執行管理操做,檢查運行實例,亦或作其餘嘗試。對MongoDB來講,mongo shell是相當重要的工具,其應用之普遍將體如今本書接下來的部分中。
運行mongo啓動shell:
$ mongo MongoDB shell version: 2.4.0 connecting to: test
啓動時,shell將自動鏈接MongoDB服務器,須確保mongod已啓動。
shell是一個功能完備的JavaScript解釋器,可運行任意JavaScript程序。爲說明這一點,咱們運行幾個簡單的數學運算:
> x = 200 200 > x / 5; 40
另外,可充分利用JavaScript的標準庫:
> Math.sin(Math.PI / 2); 1 > new Date("2010/1/1"); "Fri Jan 01 2010 00:00:00 GMT-0500 (EST)" > "Hello, World!".replace("World", "MongoDB"); Hello, MongoDB! 再者,可定義和調用JavaScript函數: > function factorial (n) { ... if (n <= 1) return 1; ... return n * factorial(n - 1); ... } > factorial(5); 120
須要注意,可以使用多行命令。shell會檢測輸入的JavaScript語句是否完整,如沒寫完可在下一行接着寫。在某行連續三次按下回車鍵可取消未輸入完成的命令,並退回到>-提示符。
能運行任意JavaScript程序聽上去很酷,不過shell的真正強大之處在於,它是一個獨立的MongoDB客戶端。啓動時,shell會連到MongoDB服務器的test數據庫,並將數據庫鏈接賦值給全局變量db。這個變量是經過shell訪問MongoDB的主要入口點。
若是想要查看db當前指向哪一個數據庫,可使用db命令:
> db test
爲了方便習慣使用SQL shell的用戶,shell還包含一些非JavaScript語法的擴展。這些擴展並不提供額外的功能,而是一些很是棒的語法糖。例如,最重要的操做之一爲選擇數據庫:
> use foobar switched to db foobar
如今,若是查看db變量,會發現其正指向foobar數據庫:
> db foobar
由於這是一個JavaScript shell,因此鍵入一個變量會將此變量的值轉換爲字符串(即數據庫名)並打印出來。
經過db變量,可訪問其中的集合。例如,經過db.baz可返回當前數據庫的baz集合。由於經過shell可訪問集合,這意味着,幾乎全部數據庫操做均可以經過shell完成。
在shell中查看或操做數據會用到4個基本操做:建立、讀取、更新和刪除(即一般所說的CRUD操做)。
insert函數可將一個文檔添加到集合中。舉一個存儲博客文章的例子。首先,建立一個名爲post的局部變量,這是一個JavaScript對象,用於表示咱們的文檔。它會有幾個鍵:"title"、"content"和"date"(發佈日期)。
> post = {"title" : "My Blog Post", ... "content" : "Here's my blog post.", ... "date" : new Date()} { "title" : "My Blog Post", "content" : "Here's my blog post.", "date" : ISODate("2012-08-24T21:12:09.982Z") }
這個對象是個有效的MongoDB文檔,因此能夠用insert方法將其保存到blog集合中:
> db.blog.insert(post)
這篇文章已被存到數據庫中。要查看它可用調用集合的find方法:
> db.blog.find() { "_id" : ObjectId("5037ee4a1084eb3ffeef7228"), "title" : "My Blog Post", "content" : "Here's my blog post.", "date" : ISODate("2012-08-24T21:12:09.982Z") }
能夠看到,咱們曾輸入的鍵/值對都已被完整地記錄。此外,還有一個額外添加的鍵"_id"。"_id"忽然出現的緣由將在本章末尾解釋。
find和findOne方法能夠用於查詢集合裏的文檔。若只想查看一個文檔,可用findOne:
> db.blog.findOne() { "_id" : ObjectId("5037ee4a1084eb3ffeef7228"), "title" : "My Blog Post", "content" : "Here's my blog post.", "date" : ISODate("2012-08-24T21:12:09.982Z") }
find和findOne能夠接受一個查詢文檔做爲限定條件。這樣就能夠查詢符合必定條件的文檔。使用find時,shell會自動顯示最多20個匹配的文檔,也可獲取更多文檔。第4章會詳細介紹查詢相關的內容。
使用update修改博客文章。update接受(至少)兩個參數:第一個是限定條件(用於匹配待更新的文檔),第二個是新的文檔。假設咱們要爲先前寫的文章增長評論功能,就須要增長一個新的鍵,用於保存評論數組。
首先,修改變量post,增長"comments"鍵:
> post.comments = [] [ ]
而後執行update操做,用新版本的文檔替換標題爲「My Blog Post」的文章:
> db.blog.update({title : "My Blog Post"}, post)
如今,文檔已經有了"comments"鍵。再用find查看一下,能夠看到新的鍵:
> db.blog.find() { "_id" : ObjectId("5037ee4a1084eb3ffeef7228"), "title" : "My Blog Post", "content" : "Here's my blog post.", "date" : ISODate("2012-08-24T21:12:09.982Z"), "comments" : [ ] }
使用remove方法可將文檔從數據庫中永久刪除。若是沒有使用任何參數,它會將集合內的全部文檔所有刪除。它能夠接受一個做爲限定條件的文檔做爲參數。例如,下面的命令會刪除剛剛建立的文章:
> db.blog.remove({title : "My Blog Post"})
如今,集合又是空的了。
上一篇文章: MongoDB指南---一、MongoDB簡介
下一篇文章: MongoDB指南---三、MongoDB基礎知識-數據類型