查詢內嵌文檔 數據準備 方式1:查詢整個內嵌文檔 與普通查詢完全相同。但是,如果要查詢一個完整的子文檔,那麼子文檔必須精確匹配(順序以及個數都要一樣)。 > db.blog.find({"comments":{"author":"lf","votes":20}}) > 方式2:只針對其鍵/值對進行查 ...
查詢內嵌文檔
數據準備
> db.blog.find().pretty() { "_id" : ObjectId("585694e4c5b0525a48a441b5"), "content" : "...", "comments" : [ { "comment" : "good post", "author" : "jim", "votes" : 15 }, { "comment" : "i thought it was too short", "author" : "yyb", "votes" : 3 }, { "comment" : "free watches", "author" : "alice", "votes" : 3 } ] } { "_id" : ObjectId("585f659898992d5a44085fc0"), "content" : "query", "comments" : [ { "comment" : "good post two", "author" : "yyb", "votes" : 10 }, { "comment" : "i thought it was vary good", "author" : "mmq", "votes" : 30 }, { "comment" : "free watches two", "author" : "lf", "votes" : 20 } ] } >
方式1:查詢整個內嵌文檔
與普通查詢完全相同。但是,如果要查詢一個完整的子文檔,那麼子文檔必須精確匹配(順序以及個數都要一樣)。
> db.blog.find({"comments":{"author":"lf","votes":20}})
>
> db.blog.find({"comments":{"comment":"free watches two","author":"lf","votes":20}}).pretty() { "_id" : ObjectId("585f659898992d5a44085fc0"), "content" : "query", "comments" : [ { "comment" : "good post two", "author" : "yyb", "votes" : 10 }, { "comment" : "i thought it was vary good", "author" : "mmq", "votes" : 30 }, { "comment" : "free watches two", "author" : "lf", "votes" : 20 } ] } >
方式2:只針對其鍵/值對進行查詢
> db.blog.find({"comments.author":"yyb","comments.votes":10}).pretty() { "_id" : ObjectId("585f659898992d5a44085fc0"), "content" : "query", "comments" : [ { "comment" : "good post two", "author" : "yyb", "votes" : 10 }, { "comment" : "i thought it was vary good", "author" : "mmq", "votes" : 30 }, { "comment" : "free watches two", "author" : "lf", "votes" : 20 } ] }
如果想要查找由yyb發表的11分以上的投票。不能直接使用以下方式
> db.blog.find({"comments":{"author":"yyb","votes":{"$gt":11}}}) >
或者
> db.blog.find({"comments.author":"yyb","comments.votes":{"$gt":11}})//本來沒有,但卻查出來兩條 { "_id" : ObjectId("585694e4c5b0525a48a441b5"), "content" : "...", "comments" : [ { "comment" : "good post", "author" : "jim", "votes" : 15 }, { "comment" : "i thought it was too short", "author" : "yyb", "votes" : 3 }, { "comment" : "free watches", "author" : "alice", "votes" : 3 } ] } { "_id" : ObjectId("585f659898992d5a44085fc0"), "content" : "query", "comments" : [ { "comment" : "good post two", "author" : "yyb", "votes" : 10 }, { "comment" : "i thought it was vary good", "author" : "mmq", "votes" : 30 }, { "comment" : "free watches two", "author" : "lf", "votes" : 20 } ] }
$elemMatch
要正確地指定一組條件,而不必指定每個鍵,就需要使用 $elemMatch 。這種模糊的命名條件句能用來在查詢條件中部分指定匹配數組中的單個內嵌文檔。
$elemMatch將限定條件進行分組,僅當需要對一個內嵌文檔的多個鍵操作時才會用到。
> db.blog.find({"comments":{"$elemMatch":{"author":"yyb","votes":{"$gt":11}}}})//無匹配結果
> db.blog.find({"comments":{"$elemMatch":{"author":"yyb","votes":{"$gte":10}}}}).pretty() { "_id" : ObjectId("585f659898992d5a44085fc0"), "content" : "query", "comments" : [ { "comment" : "good post two", "author" : "yyb", "votes" : 10 }, { "comment" : "i thought it was vary good", "author" : "mmq", "votes" : 30 }, { "comment" : "free watches two", "author" : "lf", "votes" : 20 } ] } >
$where
用它可以在查詢中執行任意的js。這樣就能在查詢中做任何事情,為安全起見,應該嚴格限制或消除$where語句的使用。應該禁止終端用戶使用任意的$where語句。
如果函數返回true,文檔就作為結果集的一部分返回;如果為false,就不返回。
使用$where在速度上要比常規查詢慢很多。每個文檔都要從BSON轉換成js對象,然後通過$where表達式來運行。而且不能使用索引。實在不得已,最好先使用常規查詢(或索引)進行過濾,然後使用$where語句,這樣組合使用可以降低性能損失。
$where語句最常見的應用就是比較文檔中的兩個鍵的值是否相等。比如:
> db.foo.insert({"apple":1,"banana":6,"pach":3}) WriteResult({ "nInserted" : 1 }) > db.foo.insert({"apple":8,"banana":4,"pach":4}) WriteResult({ "nInserted" : 1 }) > db.foo.find({"$where":function(){ ... for(var cur in this){ ... for(var other in this){ ... if(cur!=other && this[cur]==this[other]){ ... return true; ... } ... } ... } ... return false; ... }}); { "_id" : ObjectId("585f768398992d5a44085fc2"), "apple" : 8, "banana" : 4, "pach" : 4 } >
游標
資料庫使用游標返回find的執行結果。客戶端對游標的實現通常能夠對最終結果進行有效地控制。可以限制結果的數量,略過部分結果,根據任意鍵按任意順序的組合對結果進行各種排序,或者執行其他一些強大的操作。
> for(i=0;i<100;i++){ ... db.coll.insert({x:i}); ... } WriteResult({ "nInserted" : 1 }) > var cursor=db.coll.find() > while(cursor.hasNext()){ ... obj=cursor.next(); ... print(obj.x+"============"+obj._id); ... }
0============585f7c3a98992d5a44085fc3
1============585f7c3a98992d5a44085fc4
2============585f7c3a98992d5a44085fc5
3============585f7c3a98992d5a44085fc6
......
cursor.hasNext()檢查是否有後續結果存在,然後用cursor.next()獲取它。
游標類還實現了js的迭代介面,所以可以在forEach迴圈中使用:
> var coll=db.coll.find({"x":{"$lte":3}}) > coll.forEach(function(row){ ... print(row._id+"========="+row.x); ... }) 585f7d0298992d5a44086027=========0 585f7d0298992d5a44086028=========1 585f7d0298992d5a44086029=========2 585f7d0298992d5a4408602a=========3 >
調用find時,shell並不立即查詢資料庫,而是等待真正開始獲得結果時才發送查詢,這樣在執行之前可以給查詢附加額外的選項。
幾乎游標對象的每個方法都返回游標本身,這樣就可以按照任意順序組成方法鏈。例如,下麵幾種表達是等價的:
> var cursor=db.coll.find().sort({"x":-1}).skip(10).limit(1)//實現分頁的方式:先倒序,跳過10條,取1條
> var cursor=db.coll.find().limit(1).sort({"x":-1}).skip(10)
> var cursor= db.coll.find().skip(10).limit(1).sort({"x":-1})
此時,查詢還沒有真正執行,所有這些函數都只是構造查詢。
> print(cursor) DBQuery: test.coll -> { "query" : { }, "orderby" : { "x" : -1 } }
limit、sort、和skip
limit限制返回結果的數量,sort排序,skip忽略一定數量的結果。
> var cursor=db.coll.find().sort({"x":-1}).skip(10).limit(10)//實現分頁的方式:先倒序,跳過10條,取1條
> db.coll.find().limit(2) { "_id" : ObjectId("585f7d0298992d5a44086027"), "x" : 0 } { "_id" : ObjectId("585f7d0298992d5a44086028"), "x" : 1 }
......
> db.coll.find().skip(2) { "_id" : ObjectId("585f7d0298992d5a44086029"), "x" : 2 } { "_id" : ObjectId("585f7d0298992d5a4408602a"), "x" : 3 }
......
> db.coll.find().sort({"x":-1}) { "_id" : ObjectId("585f7d0298992d5a4408608a"), "x" : 99 } { "_id" : ObjectId("585f7d0298992d5a44086089"), "x" : 98 }
......
避免使用skip略過大量結果,用skip略過少量的文檔還是不錯的。但是要是數量非常多的話,skip就會變得很慢,通常可以利用上次的結果來計算下一次查詢條件。
比如可以利用最後一個文檔中的“date”的值最為查詢條件,來獲取下一頁:
> var page1=db.coll.find().sort({"date":-1}).limit(10) > var lastest=null; > while(page1.hasNext()){ latest=page1.next(); print(latest.x)} > var page2=db.coll.find({"date":{"$gt":latest.date}}).sort({"date":-1}).limit(10)
比較順序
MongoDB處理不同類型的數據是有一定順序的。有時一個鍵的值可能是多種類型的。如果對這種混合類型的鍵排序,其順序是預先定義好的。優先順序從小到大,其順序如下。
(1)最小值
(2)Null
(3)數字
(4)字元串
(5)對象/文檔
(6)數組
(7)二進位數據
(8)對象id
(9)布爾型
(10)日期型
(11)時間戳
(12)正則表達式
(13)最大值
高級查詢選項
簡單查詢
> var cursor=db.coll.find({"x":1})
封裝查詢
有一些選項可以用於對查詢進行“封裝”。
比如:
var cursor= db.coll.find({x:{“$lt”:10}}).sort({"x":-1})
實際情況不是將 {x:{“$lt”:10} 作為查詢直接發送給資料庫,而是先將查詢封裝在一個更大的文檔中。
DBQuery: test.coll -> { "query" : { "x" : { "$lt" : 10 } }, "orderby" : { "x" : -1 } }
絕大多數驅動程式都提供了輔助函數,用於向查詢中添加各種選項。比如:
> db.coll.find({"x":{"$lt":2}})._addSpecial("$showDiskLoc",true); { "_id" : ObjectId("585f7d0298992d5a44086027"), "x" : 0, "date" : ISODate("2016-12-25T10:02:02.499Z"), "$recordId" : NumberLong(104) } { "_id" : ObjectId("585f7d0298992d5a44086028"), "x" : 1, "date" : ISODate("2016-12-25T10:02:02.499Z"), "$recordId" : NumberLong(105) } >
獲取一致結果
數據處理通常做法就是先把數據從MongoDB中取出來,然後做一些變換,最後在存回去。
結果比較少時,沒什麼問題。但是如果結果集比較大,MongoDB可能會多次返回同一個文檔。原因是:體積變大的文檔,可能無法保存回原先的位置。MongoDB會為更新後無法放回原位置的文檔重新分配存儲空間。
解決方案:對查詢進行快照。這樣查詢就在_id索引上遍歷執行,這樣可以保證每個文檔只被返回一次。
> db.coll.find().snapshot()
快照會使查詢變慢,如非必要,不建議使用。
游標生命周期
看待游標的兩種角度:客戶端游標以及客戶端游標表示的資料庫游標。前面所說的游標都是客戶端游標。
在伺服器端,游標消耗記憶體和其他資源。游標遍歷盡了結果之後,獲取客戶端發來消息要求終止,資料庫就會釋放這些資源。還有一些情況導致游標終止:
- 游標完成匹配結果的迭代時。
- 客戶端的游標已經不再作用域內,驅動程式會向服務端發送一條特別的消息,讓其銷毀游標。
- 一個游標在10分鐘之內沒有使用。
資料庫命令
在shell中使用的輔助函數,都是對資料庫命令的封裝,而提供的更加簡單的介面。
> db.runCommand({"drop":"coll"}) { "ns" : "test.coll", "nIndexesWas" : 1, "ok" : 1 } > db.coll.drop() false
查看所有的資料庫命令
> db.listCommands()
資料庫命令總會返回一個包含ok鍵的文檔,如果值是1,則表示成功,反之失敗,那麼命令的返迴文檔中就會有一個額外的鍵errmsg。用於描述失敗的原因。
> db.runCommand({"drop":"coll"}) { "ok" : 0, "errmsg" : "ns not found", "code" : 26 }
MongoDB中的命令被實現為一種特殊類型的查詢,這些特殊的查詢會在$cmd集合上執行。
runCommand只是接受一個命令文檔,並且執行與這個命令文檔等價的查詢。於是,drop命令會被轉換為如下代碼:
> db.$cmd.findOne({"drop":"coll"}) { "ok" : 0, "errmsg" : "ns not found", "code" : 26 }
有些命令需要管理員許可權,而且要在admin資料庫上才能執行。如果當前位於其他的資料庫,但是需要執行一個管理員命令,可以使用 adminCommand
> db.runCommand({"shutdown":1}) { "ok" : 0, "errmsg" : "shutdown may only be run against the admin database.", "code" : 13 } > db.adminCommand({"shutdown":1}) 2016-12-25T21:32:31.474+0800 E QUERY [thread1] Error: error doing query: failed: network error while attempting to run command 'shutdown' on host '127.0.0.1:27017' : DB.prototype.runCommand@src/mongo/shell/db.js:135:1 DB.prototype.adminCommand@src/mongo/shell/db.js:153:16 @(shell):1:1