標籤:
接著上章,繼續介紹MongoDB的查詢。
Querying on Embedded Documents
有兩種方式查詢嵌入式的子Documents:查詢整個Document或者查詢個別的索引值對。
查詢整個子Document和正常的查詢是一樣的。
我們有一個document:
{ "name":{ "first":"Joe", "last":"Schmoe" }, "age":45}
我們可以尋找名為Joe Schmoe的人:
db.people.find({"name":{"first":"Joe","last":"Schmoe"}})
然而,查詢一個子Document必須精確匹配。假如Joe決定添加一個中間名欄位,那麼上面這個查詢就不成立了。
並且,這種查詢類型同樣是順序敏感,{"last":"Schmoe","first":"Joe"},這樣查詢也是不匹配的。
通常針對子文檔的key進行尋找就不會出現上述的情況了:
db.people.find({"name.last":"Schmoe","name.first":"Joe"})
下來我們來看這條資料:
{ "content":"...", "comments":[ { "author":"joe", "score":3, "comment":"nice post" }, { "author":"mary", "score":6, "comment":"terrible post" } ]}
現在我們想要尋找comments為joe並且得分大於5.
首先,我們不能這樣查詢
db.blog.find({"comments":{"author":"joe","score":{"$gte":5}}})
內嵌文檔匹配要求整個文檔完全符合,而不是匹配comments這個key。
同樣我們不能用
db.blog.find({"comments.author":"joe","comments.score":{"$gte":5}})
因為符合author條件的評論和符合score的評論可能不是同一條。也就是說會返回剛才顯示的那個document。因為"author"在第一條評論中實現,"score"在第二條評論中實現。
正確的該如何呢?
db.blog.find({"comments":{"$elemMatch":{"author":"joe","score":{"$gte":5}}}})
"$elemMatch"將限定條件進行分組,僅當需要一個內嵌文檔的多個鍵操作時才會用到。
$where Queries
索引值對是極富表現力的查詢方式,但是有些查詢他們也不能表達。
這時候,"$where"子句查詢就出場了,它允許你在你的查詢中執行任意的Javascript
最典型的應用就是比較文檔中的兩個鍵的值是否相等,例如有個條目列表,如果其中的兩個值相等則返迴文檔。例:
db.foo.insert({"apple":1,"banana":6,"peach":3})db.foo.insert({"apple":8,"spinach":4,"watermelon":4})
第二個文檔中,"spinach"和"watermelon"的值相同,所以需要返回該文檔。
db.foo.find({"$where":function(){ for(var current in this){ for(var other in this){ if(current !=other && this[current]==this[other]){ return ture; } } } return false;}})
如果函數返回為true,文檔就作為結果的一部分被返回。
在非必要時,不要用"$where":它的查詢速度比一般查詢要慢很多。
Cursors
資料庫使用遊標來返回find的執行結果。用戶端對遊標的實現通常能夠對最終結果進行有效控制。
你可以限制結果的數量,略過部分結果,根據任意方向任意鍵的組合對結果進行各種排序,或者執行其他的一些功能強大的操作。
想從Shell中建立一個遊標,首先要對集合填充一些文檔,然後對其進行查詢,並將結果分配給一個局部變數。這裡,建立一個簡單集合,而後做查詢,並用cursor儲存結果:
for(i=0;i<100;i++){ db.collection.insert({x:i});}
var cursor = db.collection.find()
這麼做的好處是一次可以查看一條結果。如果將結果放在全域變數或者沒有放在變數中,MongoDB Shell會自動迭代,自動顯示最開始的若干文檔。
也就是在這之前我們看到的種種例子,一般大家只想通過Shell看看集合裡面有什麼,而不是想在其中運行程式,這樣的設計就很合適。
要迭代結果,可以使用遊標的next方法。也可以使用hasNext來查看有沒有其他的結果。典型的遍曆如下:
while(cursor.hasNext()){ obj = cursor.next(); //do stuff}
遊標類還實現了迭代介面,所以可以在forEach迴圈中使用。
var cursor = db.people.find();cursor.forEach(function(x){ print(x.name)})
當使用find的時候,shell並不立即查詢資料庫,而是等待真正開始要求獲得結果的時候才發送查詢,這樣在執行之前可以給查詢附加額外的選項。
幾乎所有遊標對象的方法都返回遊標本身,這樣就可以按任意順序組成方法鏈。如下面幾種是等價的:
var cursor = db.foo.find().sort({"x":1}).limit(1).skip(10);var cursor = db.foo.find().limit(1).sort({"x":1}).skip(10);var cursor = db.foo.find().skip(10).limit(1).sort({"x":1});
此時,查詢還沒有執行,所有這些函數都只是構造查詢。假設我們執行
cursor.hasNext();
這時,查詢發往伺服器。shell立即擷取錢100個結果或者前4M資料(兩者之間較小者),這樣下次調用next或者hasNext時就不必興師動眾跑到伺服器上去了。
用戶端用光了第一組結果,shell會再次串連資料庫,並要求更多的結果。這個過程一直會持續到遊標耗盡或者結果全部返回。
MongoDB 學習四 : 查詢(續)