MongoDB 高級教程

MongoDB 關係

MongoDB 的關係表示多個文檔之間在邏輯上的相互聯繫。php

文檔間能夠經過嵌入和引用來創建聯繫。正則表達式

MongoDB 中的關係能夠是:mongodb

  • 1:1 (1對1)
  • 1: N (1對多)
  • N: 1 (多對1)
  • N: N (多對多)

 

接下來咱們來考慮下用戶與用戶地址的關係。數據庫

一個用戶能夠有多個地址,因此是一對多的關係。編程

如下是 user 文檔的簡單結構:數組

{
   "_id":ObjectId("52ffc33cd85242f436000001"),
   "name": "Tom Hanks",
   "contact": "987654321",
   "dob": "01-01-1991"
}

如下是 address 文檔的簡單結構:瀏覽器

{
   "_id":ObjectId("52ffc4a5d85242602e000000"),
   "building": "22 A, Indiana Apt",
   "pincode": 123456,
   "city": "Los Angeles",
   "state": "California"
} 

嵌入式關係

使用嵌入式方法,咱們能夠把用戶地址嵌入到用戶的文檔中:緩存

   "_id":ObjectId("52ffc33cd85242f436000001"),
   "contact": "987654321",
   "dob": "01-01-1991",
   "name": "Tom Benzamin",
   "address": [
      {
         "building": "22 A, Indiana Apt",
         "pincode": 123456,
         "city": "Los Angeles",
         "state": "California"
      },
      {
         "building": "170 A, Acropolis Apt",
         "pincode": 456789,
         "city": "Chicago",
         "state": "Illinois"
      }]
} 

以上數據保存在單一的文檔中,能夠比較容易的獲取和維護數據。 你能夠這樣查詢用戶的地址:安全

>db.users.findOne({"name":"Tom Benzamin"},{"address":1})

注意:以上查詢中 db 和 users 表示數據庫和集合。服務器

這種數據結構的缺點是,若是用戶和用戶地址在不斷增長,數據量不斷變大,會影響讀寫性能。

引用式關係

引用式關係是設計數據庫時常常用到的方法,這種方法把用戶數據文檔和用戶地址數據文檔分開,經過引用文檔的 id 字段來創建關係。

{
   "_id":ObjectId("52ffc33cd85242f436000001"),
   "contact": "987654321",
   "dob": "01-01-1991",
   "name": "Tom Benzamin",
   "address_ids": [
      ObjectId("52ffc4a5d85242602e000000"),
      ObjectId("52ffc4a5d85242602e000001")
   ]
}

以上實例中,用戶文檔的 address_ids 字段包含用戶地址的對象id(ObjectId)數組。

咱們能夠讀取這些用戶地址的對象id(ObjectId)來獲取用戶的詳細地址信息。

這種方法須要兩次查詢,第一次查詢用戶地址的對象id(ObjectId),第二次經過查詢的id獲取用戶的詳細地址信息。

>var result = db.users.findOne({"name":"Tom Benzamin"},{"address_ids":1})
>var addresses = db.address.find({"_id":{"$in":result["address_ids"]}})

MongoDB 數據庫引用

在上一章節MongoDB關係中咱們提到了MongoDB的引用來規範數據結構文檔。

MongoDB 引用有兩種:

  • 手動引用(Manual References)
  • DBRefs

DBRefs vs 手動引用

考慮這樣的一個場景,咱們在不一樣的集合中 (address_home, address_office, address_mailing, 等)存儲不一樣的地址(住址,辦公室地址,郵件地址等)。

這樣,咱們在調用不一樣地址時,也須要指定集合,一個文檔從多個集合引用文檔,咱們應該使用 DBRefs。


使用 DBRefs

DBRef的形式:

{ $ref : , $id : , $db : }

三個字段表示的意義爲:

  • $ref:集合名稱
  • $id:引用的id
  • $db:數據庫名稱,可選參數

如下實例中用戶數據文檔使用了 DBRef, 字段 address:

{ "_id":ObjectId("53402597d852426020000002"), "address": { "$ref": "address_home", "$id": ObjectId("534009e4d852427820000002"), "$db": "w3cschoolcc"}, "contact": "987654321", "dob": "01-01-1991", "name": "Tom Benzamin" }

address DBRef 字段指定了引用的地址文檔是在 address_home 集合下的 w3cschoolcc 數據庫,id 爲 534009e4d852427820000002。

如下代碼中,咱們經過指定 $ref 參數(address_home 集合)來查找集合中指定id的用戶地址信息:

>var user = db.users.findOne({"name":"Tom Benzamin"}) >var dbRef = user.address >db[dbRef.$ref].findOne({"_id":(dbRef.$id)})

以上實例返回了 address_home 集合中的地址數據:

{ "_id" : ObjectId("534009e4d852427820000002"), "building" : "22 A, Indiana Apt", "pincode" : 123456, "city" : "Los Angeles", "state" : "California" }

MongoDB 覆蓋索引查詢

官方的MongoDB的文檔中說明,覆蓋查詢是如下的查詢:

  • 全部的查詢字段是索引的一部分
  • 全部的查詢返回字段在同一個索引中

因爲全部出如今查詢中的字段是索引的一部分, MongoDB 無需在整個數據文檔中檢索匹配查詢條件和返回使用相同索引的查詢結果。

由於索引存在於RAM中,從索引中獲取數據比經過掃描文檔讀取數據要快得多。


使用覆蓋索引查詢

爲了測試蓋索引查詢,使用如下 users 集合:

{
   "_id": ObjectId("53402597d852426020000002"),
   "contact": "987654321",
   "dob": "01-01-1991",
   "gender": "M",
   "name": "Tom Benzamin",
   "user_name": "tombenzamin"
}

咱們在 users 集合中建立聯合索引,字段爲 gender 和 user_name :

>db.users.ensureIndex({gender:1,user_name:1})

如今,該索引會覆蓋如下查詢:

>db.users.find({gender:"M"},{user_name:1,_id:0})

也就是說,對於上述查詢,MongoDB的不會去數據庫文件中查找。相反,它會從索引中提取數據,這是很是快速的數據查詢。

因爲咱們的索引中不包括 _id 字段,_id在查詢中會默認返回,咱們能夠在MongoDB的查詢結果集中排除它。

下面的實例沒有排除_id,查詢就不會被覆蓋:

>db.users.find({gender:"M"},{user_name:1})

最後,若是是如下的查詢,不能使用覆蓋索引查詢:

      • 全部索引字段是一個數組
      • 全部索引字段是一個子文檔

 

MongoDB 查詢分析

MongoDB 查詢分析能夠確保咱們建議的索引是否有效,是查詢語句性能分析的重要工具。

MongoDB 查詢分析經常使用函數有:explain() 和 hint()。


使用 explain()

explain 操做提供了查詢信息,使用索引及查詢統計等。有利於咱們對索引的優化。

接下來咱們在 users 集合中建立 gender 和 user_name 的索引:

>db.users.ensureIndex({gender:1,user_name:1})
</p>
<p>如今在查詢語句中使用 explain :</p>
<pre>
>db.users.find({gender:"M"},{user_name:1,_id:0}).explain()

以上的 explain() 查詢返回以下結果:

{
   "cursor" : "BtreeCursor gender_1_user_name_1",
   "isMultiKey" : false,
   "n" : 1,
   "nscannedObjects" : 0,
   "nscanned" : 1,
   "nscannedObjectsAllPlans" : 0,
   "nscannedAllPlans" : 1,
   "scanAndOrder" : false,
   "indexOnly" : true,
   "nYields" : 0,
   "nChunkSkips" : 0,
   "millis" : 0,
   "indexBounds" : {
      "gender" : [
         [
            "M",
            "M"
         ]
      ],
      "user_name" : [
         [
            {
               "$minElement" : 1
            },
            {
               "$maxElement" : 1
            }
         ]
      ]
   }
}

如今,咱們看看這個結果集的字段:

        • indexOnly: 字段爲 true ,表示咱們使用了索引。
        • cursor:由於這個查詢使用了索引,MongoDB 中索引存儲在B樹結構中,因此這是也使用了 BtreeCursor 類型的遊標。若是沒有使用索引,遊標的類型是 BasicCursor。這個鍵還會給出你所使用的索引的名稱,你經過這個名稱能夠查看當前數據庫下的system.indexes集合(系統自動建立,因爲存儲索引信息,這個稍微會提到)來獲得索引的詳細信息。
        • n:當前查詢返回的文檔數量。
        • nscanned/nscannedObjects:代表當前此次查詢一共掃描了集合中多少個文檔,咱們的目的是,讓這個數值和返回文檔的數量越接近越好。
        • millis:當前查詢所需時間,毫秒數。
        • indexBounds:當前查詢具體使用的索引。

使用 hint()

雖然MongoDB查詢優化器通常工做的很不錯,可是也可使用 hint 來強制 MongoDB 使用一個指定的索引。

這種方法某些情形下會提高性能。 一個有索引的 collection 而且執行一個多字段的查詢(一些字段已經索引了)。

以下查詢實例指定了使用 gender 和 user_name 索引字段來查詢:

>db.users.find({gender:"M"},{user_name:1,_id:0}).hint({gender:1,user_name:1})

可使用 explain() 函數來分析以上查詢:

>db.users.find({gender:"M"},{user_name:1,_id:0}).hint({gender:1,user_name:1}).explain()

MongoDB 原子操做

mongodb不支持事務,因此,在你的項目中應用時,要注意這點。不管什麼設計,都不要要求mongodb保證數據的完整性。

可是mongodb提供了許多原子操做,好比文檔的保存,修改,刪除等,都是原子操做。

所謂原子操做就是要麼這個文檔保存到Mongodb,要麼沒有保存到Mongodb,不會出現查詢到的文檔沒有保存完整的狀況。


原子操做數據模型

考慮下面的例子,圖書館的書籍及結帳信息。

實例說明了在一個相同的文檔中如何確保嵌入字段關聯原子操做(update:更新)的字段是同步的。

book = { _id: 123456789, title: "MongoDB: The Definitive Guide", author: [ "Kristina Chodorow", "Mike Dirolf" ], published_date: ISODate("2010-09-24"), pages: 216, language: "English", publisher_id: "oreilly", available: 3, checkout: [ { by: "joe", date: ISODate("2012-10-15") } ] }

你可使用 db.collection.findAndModify() 方法來判斷書籍是否可結算並更新新的結算信息。

在同一個文檔中嵌入的 available 和 checkout 字段來確保這些字段是同步更新的:

db.books.findAndModify ( { query: { _id: 123456789, available: { $gt: 0 } }, update: { $inc: { available: -1 }, $push: { checkout: { by: "abc", date: new Date() } } } } )

原子操做經常使用命令

$set

用來指定一個鍵並更新鍵值,若鍵不存在並建立。

{ $set : { field : value } }

$unset

用來刪除一個鍵。

{ $unset : { field : 1} }

$inc

$inc能夠對文檔的某個值爲數字型(只能爲知足要求的數字)的鍵進行增減的操做。

{ $inc : { field : value } }

$push

用法:

{ $push : { field : value } }

把value追加到field裏面去,field必定要是數組類型才行,若是field不存在,會新增一個數組類型加進去。

$pushAll

同$push,只是一次能夠追加多個值到一個數組字段內。

{ $pushAll : { field : value_array } }

$pull

從數組field內刪除一個等於value值。

{ $pull : { field : _value } }

$addToSet

增長一個值到數組內,並且只有當這個值不在數組內才增長。

$pop

刪除數組的第一個或最後一個元素

{ $pop : { field : 1 } }

$rename

修改字段名稱

{ $rename : { old_field_name : new_field_name } }

$bit

位操做,integer類型

{$bit : { field : {and : 5}}}

偏移操做符

> t.find() { "_id" : ObjectId("4b97e62bf1d8c7152c9ccb74"), "title" : "ABC", "comments" : [ { "by" : "joe", "votes" : 3 }, { "by" : "jane", "votes" : 7 } ] } > t.update( {'comments.by':'joe'}, {$inc:{'comments.$.votes':1}}, false, true ) > t.find() { "_id" : ObjectId("4b97e62bf1d8c7152c9ccb74"), "title" : "ABC", "comments" : [ { "by" : "joe", "votes" : 4 }, { "by" : "jane", "votes" : 7 } ] }


MongoDB 高級索引

考慮如下文檔集合(users ):

{
   "address": {
      "city": "Los Angeles",
      "state": "California",
      "pincode": "123"
   },
   "tags": [
      "music",
      "cricket",
      "blogs"
   ],
   "name": "Tom Benzamin"
}

以上文檔包含了 address 子文檔和 tags 數組。


索引數組字段

假設咱們基於標籤來檢索用戶,爲此咱們須要對集合中的數組 tags 創建索引。

在數組中建立索引,須要對數組中的每一個字段依次創建索引。因此在咱們爲數組 tags 建立索引時,會爲 music、cricket、blogs三個值創建單獨的索引。

使用如下命令建立數組索引:

>db.users.ensureIndex({"tags":1})

建立索引後,咱們能夠這樣檢索集合的 tags 字段:

>db.users.find({tags:"cricket"})

爲了驗證咱們使用使用了索引,可使用 explain 命令:

>db.users.find({tags:"cricket"}).explain()

以上命令執行結果中會顯示 "cursor" : "BtreeCursor tags_1" ,則表示已經使用了索引。


索引子文檔字段

假設咱們須要經過city、state、pincode字段來檢索文檔,因爲這些字段是子文檔的字段,因此咱們須要對子文檔創建索引。

爲子文檔的三個字段建立索引,命令以下:

>db.users.ensureIndex({"address.city":1,"address.state":1,"address.pincode":1})

一旦建立索引,咱們可使用子文檔的字段來檢索數據:

>db.users.find({"address.city":"Los Angeles"})   

記住查詢表達式必須遵循指定的索引的順序。因此上面建立的索引將支持如下查詢:

>db.users.find({"address.city":"Los Angeles","address.state":"California"}) 

一樣支持如下查詢:

>db.users.find({"address.city":"LosAngeles","address.state":"California","address.pincode":"123"})


MongoDB 索引限制


額外開銷

每一個索引佔據必定的存儲空間,在進行插入,更新和刪除操做時也須要對索引進行操做。因此,若是你不多對集合進行讀取操做,建議不使用索引。


內存(RAM)使用

因爲索引是存儲在內存(RAM)中,你應該確保該索引的大小不超過內存的限制。

若是索引的大小大於內存的限制,MongoDB會刪除一些索引,這將致使性能降低。


查詢限制

索引不能被如下的查詢使用:

  • 正則表達式及非操做符,如 $nin, $not, 等。
  • 算術運算符,如 $mod, 等。
  • $where 子句

因此,檢測你的語句是否使用索引是一個好的習慣,能夠用explain來查看。


索引鍵限制

從2.6版本開始,若是現有的索引字段的值超過索引鍵的限制,MongoDB中不會建立索引。


插入文檔超過索引鍵限制

若是文檔的索引字段值超過了索引鍵的限制,MongoDB不會將任何文檔轉換成索引的集合。與mongorestore和mongoimport工具相似。


最大範圍

  • 集合中索引不能超過64個
  • 索引名的長度不能超過128個字符
  • 一個複合索引最多能夠有31個字段

 

MongoDB ObjectId


在前面幾個章節中咱們已經使用了MongoDB 的對象 Id(ObjectId)。

在本章節中,咱們將瞭解的ObjectId的結構。

ObjectId 是一個12字節 BSON 類型數據,有如下格式:

  • 前4個字節表示時間戳
  • 接下來的3個字節是機器標識碼
  • 緊接的兩個字節由進程id組成(PID)
  • 最後三個字節是隨機數。

MongoDB中存儲的文檔必須有一個"_id"鍵。這個鍵的值能夠是任何類型的,默認是個ObjectId對象。

在一個集合裏面,每一個文檔都有惟一的"_id"值,來確保集合裏面每一個文檔都能被惟一標識。

MongoDB採用ObjectId,而不是其餘比較常規的作法(好比自動增長的主鍵)的主要緣由,由於在多個 服務器上同步自動增長主鍵值既費力還費時。


建立新的ObjectId

使用如下代碼生成新的ObjectId:

>newObjectId = ObjectId()

上面的語句返回如下惟一輩子成的id:

ObjectId("5349b4ddd2781d08c09890f3")

你也可使用生成的id來取代MongoDB自動生成的ObjectId:

>myObjectId = ObjectId("5349b4ddd2781d08c09890f4")

建立文檔的時間戳

因爲 ObjectId 中存儲了 4 個字節的時間戳,因此你不須要爲你的文檔保存時間戳字段,你能夠經過 getTimestamp 函數來獲取文檔的建立時間:

>ObjectId("5349b4ddd2781d08c09890f4").getTimestamp()

以上代碼將返回 ISO 格式的文檔建立時間:

ISODate("2014-04-12T21:49:17Z")

ObjectId 轉換爲字符串

在某些狀況下,您可能須要將ObjectId轉換爲字符串格式。你可使用下面的代碼:

>new ObjectId().str

以上代碼將返回Guid格式的字符串::

5349b4ddd2781d08c09890f3

MongoDB Map Reduce

Map-Reduce是一種計算模型,簡單的說就是將大批量的工做(數據)分解(MAP)執行,而後再將結果合併成最終結果(REDUCE)。

MongoDB提供的Map-Reduce很是靈活,對於大規模數據分析也至關實用。

MapReduce 命令

如下是MapReduce的基本語法:

>db.collection.mapReduce(
   function() {emit(key,value);},  //map 函數
   function(key,values) {return reduceFunction},   //reduce 函數
   {
      out: collection,
      query: document,
      sort: document,
      limit: number
   }
)

使用 MapReduce 要實現兩個函數 Map 函數和 Reduce 函數,Map 函數調用 emit(key, value), 遍歷 collection 中全部的記錄, 將 key 與 value 傳遞給 Reduce 函數進行處理。

Map 函數必須調用 emit(key, value) 返回鍵值對。

參數說明:

  • map :映射函數 (生成鍵值對序列,做爲 reduce 函數參數)。
  • reduce 統計函數,reduce函數的任務就是將key-values變成key-value,也就是把values數組變成一個單一的值value。。
  • out 統計結果存放集合 (不指定則使用臨時集合,在客戶端斷開後自動刪除)。
  • query 一個篩選條件,只有知足條件的文檔纔會調用map函數。(query。limit,sort能夠隨意組合)
  • sort 和limit結合的sort排序參數(也是在發往map函數前給文檔排序),能夠優化分組機制
  • limit 發往map函數的文檔數量的上限(要是沒有limit,單獨使用sort的用處不大)

如下實例在集合 orders 中查找 status:"A" 的數據,並根據 cust_id 來分組,並計算 amount 的總和。


使用 MapReduce

考慮如下文檔結構存儲用戶的文章,文檔存儲了用戶的 user_name 和文章的 status 字段:

>db.posts.insert({
   "post_text": "菜鳥教程,最全的技術文檔。",
   "user_name": "mark",
   "status":"active"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
   "post_text": "菜鳥教程,最全的技術文檔。",
   "user_name": "mark",
   "status":"active"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
   "post_text": "菜鳥教程,最全的技術文檔。",
   "user_name": "mark",
   "status":"active"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
   "post_text": "菜鳥教程,最全的技術文檔。",
   "user_name": "mark",
   "status":"active"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
   "post_text": "菜鳥教程,最全的技術文檔。",
   "user_name": "mark",
   "status":"disabled"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
   "post_text": "菜鳥教程,最全的技術文檔。",
   "user_name": "runoob",
   "status":"disabled"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
   "post_text": "菜鳥教程,最全的技術文檔。",
   "user_name": "runoob",
   "status":"disabled"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
   "post_text": "菜鳥教程,最全的技術文檔。",
   "user_name": "runoob",
   "status":"active"
})
WriteResult({ "nInserted" : 1 })

如今,咱們將在 posts 集合中使用 mapReduce 函數來選取已發佈的文章(status:"active"),並經過user_name分組,計算每一個用戶的文章數:

>db.posts.mapReduce( 
   function() { emit(this.user_name,1); }, 
   function(key, values) {return Array.sum(values)}, 
      {  
         query:{status:"active"},  
         out:"post_total" 
      }
)

以上 mapReduce 輸出結果爲:

{
        "result" : "post_total",
        "timeMillis" : 23,
        "counts" : {
                "input" : 5,
                "emit" : 5,
                "reduce" : 1,
                "output" : 2
        },
        "ok" : 1
}

結果代表,共有 5 個符合查詢條件(status:"active")的文檔, 在map函數中生成了 5 個鍵值對文檔,最後使用reduce函數將相同的鍵值分爲 2 組。

 

具體參數說明:

  • result:儲存結果的collection的名字,這是個臨時集合,MapReduce的鏈接關閉後自動就被刪除了。
  • timeMillis:執行花費的時間,毫秒爲單位
  • input:知足條件被髮送到map函數的文檔個數
  • emit:在map函數中emit被調用的次數,也就是全部集合中的數據總量
  • ouput:結果集合中的文檔個數(count對調試很是有幫助)
  • ok:是否成功,成功爲1
  • err:若是失敗,這裏能夠有失敗緣由,不過從經驗上來看,緣由比較模糊,做用不大

使用 find 操做符來查看 mapReduce 的查詢結果:

>db.posts.mapReduce( 
   function() { emit(this.user_name,1); }, 
   function(key, values) {return Array.sum(values)}, 
      {  
         query:{status:"active"},  
         out:"post_total" 
      }
).find()

以上查詢顯示以下結果,兩個用戶 tom 和 mark 有兩個發佈的文章:

{ "_id" : "mark", "value" : 4 }
{ "_id" : "runoob", "value" : 1 }

用相似的方式,MapReduce能夠被用來構建大型複雜的聚合查詢。

Map函數和Reduce函數可使用 JavaScript 來實現,使得MapReduce的使用很是靈活和強大。

 

MongoDB 全文檢索

全文檢索對每個詞創建一個索引,指明該詞在文章中出現的次數和位置,當用戶查詢時,檢索程序就根據事先創建的索引進行查找,並將查找的結果反饋給用戶的檢索方式。

這個過程相似於經過字典中的檢索字表查字的過程。

MongoDB 從 2.4 版本開始支持全文檢索,目前支持15種語言(暫時不支持中文)的全文索引。

  • danish
  • dutch
  • english
  • finnish
  • french
  • german
  • hungarian
  • italian
  • norwegian
  • portuguese
  • romanian
  • russian
  • spanish
  • swedish
  • turkish

啓用全文檢索

MongoDB 在 2.6 版本之後是默認開啓全文檢索的,若是你使用以前的版本,你須要使用如下代碼來啓用全文檢索:

>db.adminCommand({setParameter:true,textSearchEnabled:true})

或者使用命令:

mongod --setParameter textSearchEnabled=true

建立全文索引

考慮如下 posts 集合的文檔數據,包含了文章內容(post_text)及標籤(tags):

{
   "post_text": "enjoy the mongodb articles on Runoob",
   "tags": [
      "mongodb",
      "runoob"
   ]
}

咱們能夠對 post_text 字段創建全文索引,這樣咱們能夠搜索文章內的內容:

>db.posts.ensureIndex({post_text:"text"})

使用全文索引

如今咱們已經對 post_text 創建了全文索引,咱們能夠搜索文章中的關鍵詞 runoob:

>db.posts.find({$text:{$search:"runoob"}})

如下命令返回了以下包含 runoob 關鍵詞的文檔數據:

{ 
   "_id" : ObjectId("53493d14d852429c10000002"), 
   "post_text" : "enjoy the mongodb articles on Runoob", 
   "tags" : [ "mongodb", "runoob" ]
}

若是你使用的是舊版本的 MongoDB,你可使用如下命令:

>db.posts.runCommand("text",{search:"runoob"})

使用全文索引能夠提升搜索效率。


刪除全文索引

刪除已存在的全文索引,可使用 find 命令查找索引名:

>db.posts.getIndexes()

經過以上命令獲取索引名,本例的索引名爲post_text_text,執行如下命令來刪除索引:

>db.posts.dropIndex("post_text_text")


MongoDB 正則表達式

正則表達式是使用單個字符串來描述、匹配一系列符合某個句法規則的字符串。

許多程序設計語言都支持利用正則表達式進行字符串操做。

MongoDB 使用 $regex 操做符來設置匹配字符串的正則表達式。

MongoDB使用PCRE (Perl Compatible Regular Expression) 做爲正則表達式語言。

不一樣於全文檢索,咱們使用正則表達式不須要作任何配置。

考慮如下 posts 集合的文檔結構,該文檔包含了文章內容和標籤:

{
   "post_text": "enjoy the mongodb articles on runoob",
   "tags": [
      "mongodb",
      "runoob"
   ]
}

使用正則表達式

如下命令使用正則表達式查找包含 runoob 字符串的文章:

>db.posts.find({post_text:{$regex:"runoob"}})

以上查詢也能夠寫爲:

>db.posts.find({post_text:/runoob/})

不區分大小寫的正則表達式

若是檢索須要不區分大小寫,咱們能夠設置 $options 爲 $i。

如下命令將查找不區分大小寫的字符串 runoob:

>db.posts.find({post_text:{$regex:"runoob",$options:"$i"}})

集合中會返回全部包含字符串 runoob 的數據,且不區分大小寫:

{
   "_id" : ObjectId("53493d37d852429c10000004"),
   "post_text" : "hey! this is my post on  runoob", 
   "tags" : [ "runoob" ]
} 

數組元素使用正則表達式

咱們還能夠在數組字段中使用正則表達式來查找內容。 這在標籤的實現上很是有用,若是你須要查找包含以 run 開頭的標籤數據(ru 或 run 或 runoob), 你可使用如下代碼:

>db.posts.find({tags:{$regex:"run"}})

優化正則表達式查詢

  • 若是你的文檔中字段設置了索引,那麼使用索引相比於正則表達式匹配查找全部的數據查詢速度更快。

     

  • 若是正則表達式是前綴表達式,全部匹配的數據將以指定的前綴字符串爲開始。例如: 若是正則表達式爲 ^tut ,查詢語句將查找以 tut 爲開頭的字符串。

這裏面使用正則表達式有兩點須要注意:

正則表達式中使用變量。必定要使用eval將組合的字符串進行轉換,不能直接將字符串拼接後傳入給表達式。不然沒有報錯信息,只是結果爲空!實例以下:

var name=eval("/" + 變量值key +"/i"); 

如下是模糊查詢包含title關鍵詞, 且不區分大小寫:

title:eval("/"+title+"/i")    // 等同於 title:{$regex:title,$Option:"$i"}   


MongoDB 管理工具: Rockmongo

RockMongo是PHP5寫的一個MongoDB管理工具。

經過 Rockmongo 你能夠管理 MongoDB服務,數據庫,集合,文檔,索引等等。

它提供了很是人性化的操做。相似 phpMyAdmin(PHP開發的MySql管理工具)。

Rockmongo 下載地址:http://rockmongo.com/downloads Rockmongo 管理工具


簡介

主要特徵:

  • 使用寬鬆的New BSD License協議
  • 速度快,安裝簡單
  • 支持多語言(目前提供中文、英文、日文、巴西葡萄牙語、法語、德語、俄語、意大利語)
  • 系統
    • 能夠配置多個主機,每一個主機能夠有多個管理員
    • 須要管理員密碼才能登入操做,確保數據庫的安全性
  • 服務器
    • 服務器信息 (WEB服務器, PHP, PHP.ini相關指令 ...)
    • 狀態
    • 數據庫信息
  • 數據庫
    • 查詢,建立和刪除
    • 執行命令和Javascript代碼
    • 統計信息
  • 集合(至關於表)
    • 強大的查詢工具
    • 讀數據,寫數據,更改數據,複製數據,刪除數據
    • 查詢、建立和刪除索引
    • 清空數據
    • 批量刪除和更改數據
    • 統計信息
  • GridFS
    • 查看分塊
    • 下載文件

安裝

需求

  • 一個能運行PHP的Web服務器,好比Apache Httpd, Nginx ...
  • PHP - 須要PHP v5.1.6或更高版本,須要支持SESSION
    • 爲了能鏈接MongoDB,你須要安裝php_mongo擴展

快速安裝

  • 下載安裝包
  • 解壓到你的網站目錄下
  • 用編輯器打開config.php,修改host, port, admins等參數
  • 在瀏覽器中訪問index.php,好比說:http://localhost/rockmongo/index.php
  • 使用用戶名和密碼登陸,默認爲"admin"和"admin"
  • 開始玩轉MongoDB!

參考文章:http://rockmongo.com/wiki/introduction?lang=zh_cn

 

MongoDB GridFS

GridFS 用於存儲和恢復那些超過16M(BSON文件限制)的文件(如:圖片、音頻、視頻等)。

GridFS 也是文件存儲的一種方式,可是它是存儲在MonoDB的集合中。

GridFS 能夠更好的存儲大於16M的文件。

GridFS 會將大文件對象分割成多個小的chunk(文件片斷),通常爲256k/個,每一個chunk將做爲MongoDB的一個文檔(document)被存儲在chunks集合中。

 

GridFS 用兩個集合來存儲一個文件:fs.files與fs.chunks。

每一個文件的實際內容被存在chunks(二進制數據)中,和文件有關的meta數據(filename,content_type,還有用戶自定義的屬性)將會被存在files集合中。

如下是簡單的 fs.files 集合文檔:

{
   "filename": "test.txt",
   "chunkSize": NumberInt(261120),
   "uploadDate": ISODate("2014-04-13T11:32:33.557Z"),
   "md5": "7b762939321e146569b07f72c62cca4f",
   "length": NumberInt(646)
}

如下是簡單的 fs.chunks 集合文檔:

{
   "files_id": ObjectId("534a75d19f54bfec8a2fe44b"),
   "n": NumberInt(0),
   "data": "Mongo Binary Data"
}

GridFS 添加文件

如今咱們使用 GridFS 的 put 命令來存儲 mp3 文件。 調用 MongoDB 安裝目錄下bin的 mongofiles.exe工具。

打開命令提示符,進入到MongoDB的安裝目錄的bin目錄中,找到mongofiles.exe,並輸入下面的代碼:

>mongofiles.exe -d gridfs put song.mp3

GridFS 是存儲文件的數據名稱。若是不存在該數據庫,MongoDB會自動建立。Song.mp3 是音頻文件名。

 

使用如下命令來查看數據庫中文件的文檔:

>db.fs.files.find()

以上命令執行後返回如下文檔數據:

{
   _id: ObjectId('534a811bf8b4aa4d33fdf94d'), 
   filename: "song.mp3", 
   chunkSize: 261120, 
   uploadDate: new Date(1397391643474), md5: "e4f53379c909f7bed2e9d631e15c1c41",
   length: 10401959 
}

咱們能夠看到 fs.chunks 集合中全部的區塊,如下咱們獲得了文件的 _id 值,咱們能夠根據這個 _id 獲取區塊(chunk)的數據:

>db.fs.chunks.find({files_id:ObjectId('534a811bf8b4aa4d33fdf94d')})

以上實例中,查詢返回了 40 個文檔的數據,意味着mp3文件被存儲在40個區塊中。

 

MongoDB 固定集合(Capped Collections)

MongoDB 固定集合(Capped Collections)是性能出色且有着固定大小的集合,對於大小固定,咱們能夠想象其就像一個環形隊列,當集合空間用完後,再插入的元素就會覆蓋最初始的頭部的元素!


建立固定集合

咱們經過createCollection來建立一個固定集合,且capped選項設置爲true:

>db.createCollection("cappedLogCollection",{capped:true,size:10000})

還能夠指定文檔個數,加上max:1000屬性:

>db.createCollection("cappedLogCollection",{capped:true,size:10000,max:1000})

判斷集合是否爲固定集合:

>db.cappedLogCollection.isCapped()

若是須要將已存在的集合轉換爲固定集合可使用如下命令:

>db.runCommand({"convertToCapped":"posts",size:10000})

以上代碼將咱們已存在的 posts 集合轉換爲固定集合。


固定集合查詢

固定集合文檔按照插入順序儲存的,默認狀況下查詢就是按照插入順序返回的,也可使用$natural調整返回順序。

>db.cappedLogCollection.find().sort({$natural:-1})

固定集合的功能特色

能夠插入及更新,但更新不能超出collection的大小,不然更新失敗,不容許刪除,可是能夠調用drop()刪除集合中的全部行,可是drop後須要顯式地重建集合。

在32位機子上一個cappped collection的最大值約爲482.5M,64位上只受系統文件大小的限制。


固定集合屬性及用法

屬性

  • 屬性1:對固定集合進行插入速度極快
  • 屬性2:按照插入順序的查詢輸出速度極快
  • 屬性3:可以在插入最新數據時,淘汰最先的數據

用法

  • 用法1:儲存日誌信息
  • 用法2:緩存一些少許的文檔

MongoDB 自動增加

MongoDB 沒有像 SQL 同樣有自動增加的功能, MongoDB 的 _id 是系統自動生成的12字節惟一標識。

但在某些狀況下,咱們可能須要實現 ObjectId 自動增加功能。

因爲 MongoDB 沒有實現這個功能,咱們能夠經過編程的方式來實現,如下咱們將在 counters 集合中實現_id字段自動增加。


使用 counters 集合

考慮如下 products 文檔。咱們但願 _id 字段實現 從 1,2,3,4 到 n 的自動增加功能。

{
  "_id":1,
  "product_name": "Apple iPhone",
  "category": "mobiles"
}

爲此,建立 counters 集合,序列字段值能夠實現自動長:

>db.createCollection("counters")

如今咱們向 counters 集合中插入如下文檔,使用 productid 做爲 key:

{
  "_id":"productid",
  "sequence_value": 0
}

sequence_value 字段是序列經過自動增加後的一個值。

使用如下命令插入 counters 集合的序列文檔中:

>db.counters.insert({_id:"productid",sequence_value:0})

建立 Javascript 函數

如今,咱們建立函數 getNextSequenceValue 來做爲序列名的輸入, 指定的序列會自動增加 1 並返回最新序列值。在本文的實例中序列名爲 productid 。

>function getNextSequenceValue(sequenceName){
   var sequenceDocument = db.counters.findAndModify(
      {
         query:{_id: sequenceName },
         update: {$inc:{sequence_value:1}},
         new:true
      });
   return sequenceDocument.sequence_value;
}

使用 Javascript 函數

接下來咱們將使用 getNextSequenceValue 函數建立一個新的文檔, 並設置文檔 _id 自動爲返回的序列值:

>db.products.insert({
   "_id":getNextSequenceValue("productid"),
   "product_name":"Apple iPhone",
   "category":"mobiles"})

>db.products.insert({
   "_id":getNextSequenceValue("productid"),
   "product_name":"Samsung S3",
   "category":"mobiles"})

就如你所看到的,咱們使用 getNextSequenceValue 函數來設置 _id 字段。

爲了驗證函數是否有效,咱們可使用如下命令讀取文檔:

>db.products.find()

以上命令將返回如下結果,咱們發現 _id 字段是自增加的:

{ "_id" : 1, "product_name" : "Apple iPhone", "category" : "mobiles"}

{ "_id" : 2, "product_name" : "Samsung S3", "category" : "mobiles" }
相關文章
相關標籤/搜索