MongoDB中使用find來進行查詢。查詢就是返回一個集合中文檔的子集,子集合的范圍從0個文檔到整個集合。find的第一個參數決定了要返回哪些文檔,其形式也是一個文檔,說明要執行的查詢細節。
空的查詢文檔{}會匹配集合的全部內容。要是不指定查詢文檔,默認就是{}。
例如:
> db.c.find()
將返回集合c中的所有內容。
查找所有"age"的值為27的文檔:
> db.users.find({"age" : 27})
查找所有值為"joe"的"username"鍵:
> db.users.find({"username" : "joe"})
可以通過向查詢文檔加入多個鍵/值對的方式來將多個查詢條件組合在一起。例如,查詢所有用戶名為"joe"且年齡為27歲的用戶:
> db.users.find({"username" : "joe", "age" : 27})
『指定返回的鍵』
有時并不需要將文檔中的所有鍵/值對都返回。遇到這種情況,可以通過find(或者findOne)的第二個參數來指定想要的鍵。
例如,如果只對用戶集合的"username"和"email"鍵感興趣,可以使用如下查詢返回這些鍵:
> db.users.find({}, {"username" : 1, "email" : 1})
也可以用第二個參數來剔除查詢結果中的某個鍵/值對。例如,文檔中有很多鍵,但是不希望結果中含有"fatal_weakness"鍵:
> db.users.find({}, {"fatal_weakness" : 0})
也可以用來防止返回"_id":
> db.users.find({}, {"username" : 1, "_id" : 0})
查詢條件
比較操作符"$lt"、"$lte"、"$gt"、"$gte"分別對應<、<=、>、>=。
例:查詢在18~30歲(含)的用戶:
> db.users.find({"age" : {"$gte" : 18, "$lte" : 30}})
查詢在2007年1月1日前注冊的人:
> start = new Date("01/01/2007")
> db.users.find({"registered" : {"$lt" : start}})
使用條件操作符"$ne"表示"不相等"。
例:查詢所有名字不為"joe"的用戶:
> db.users.find({"username" : {"$ne" : "joe"}})
"$ne"能用于所有類型的數據。
『OR查詢』
MongoDB中有兩種方式進行OR查詢:"$in"用來查詢一個鍵的多個值;"$or"用來完成多個鍵值的任意給定值。
對于單一鍵要是有多個值與其匹配的話,就要用"$in"加一個條件數組。例如,抽獎活動的中獎號碼是725、542和390.要找出全部這些中獎數據,可以構建如下查詢:
> db.raffle.find({"ticket_no" : {"$in" : [725, 542, 390]}})
"$in"可以指定不同的類型的條件和值。例如,在逐步將用戶名的ID號遷移成用戶名的過程中,要做兼顧二者的查詢:
> db.users.find({"user_id" : {"$in" : [12345, "joe"]}})
這會匹配"user_id"等于12345的文檔,也會匹配"user_id"等于"joe"的文檔。
如果"$in"對應的數組只有一個值,那么和直接匹配這個值效果是一樣的。例如,{ticket_no : {$in : [725]}}等價于{ticket_no : {$in : 725}}。
與"$in"相對的是"$nin",將返回與數組中所有條件都不匹配的文檔。要是想返回所有沒有中獎的人,就可以用如下方法進行查詢:
> db.raffle.find({"ticket_no" : {"$nin" : [725, 542, 390]}})
查詢將會返回沒有那些號碼的人。
"$or"接受一個包含所有可能條件的數組作為參數。例:找"ticket_no"為725或者"winner"為true的文檔:
> db.raffle.find({"$or" : [{"ticket_no" : 725}, {"winner" : true}]})
"$or"可以含有其他條件句。例如,如果想要將"ticket_no"與那三個值匹配上,外加"winner"鍵,就可以這么做:
> db.raffle.find({"$or" : [{"ticket_no" : {"$in" : [725, 542, 390]}}, {"winner" : true}]})
『$not』
"$not"是元條件句,即可以用在任何其他條件之上。例:對于取模運算符"$mod"來說。"$mod"會將查詢得知除以第一個給定值,若余數等于第二個給定值則返回該結果:
> db.users.find({"id_num" : {"$mod" : [5, 1]}})
上面的結果會返回"id_num"值為1、6/11/16等的用戶。如果要返回"id_num"為2、3、4、5、7、8、9、10、12等的用戶,則應使用"$not":
> db.users.find("id_num" : {"$not" : {"$mod" : [5, 1]}})
!"$not"與正則表達式聯合使用的時候極為有用,用來查找那些與特定模式不符的文檔。
『條件句的規則』
條件句是內層文檔的鍵,而修改器則是外層文檔的鍵。
一個鍵可以由多個條件,但是一個鍵不能對應多個更新修改器。
『特定于類型的查詢』
"null"不僅能匹配自身,而且能匹配"不存在的"。
如果僅僅想要匹配鍵值為null的文檔,既要檢查該鍵的值是否為null,還要通過"$exists"條件判定值已經已存在:
> db.c.find({"z" : {"$in" : [null], "$exists" : true}})
『正則表達式』
例:想要查找所有名為Joe或者joe的用戶,就可以使用正則表達式執行忽略大小寫的匹配:
> db.users.find({"name" : /joe/i})
匹配各種大小寫的joe以及joey:
> db.users.find({"name" : /joe?/i})
MongoDB使用Perl兼容的正則表達式(PCRE)庫來匹配正則表達式,PCRE支持的正則表達式語法都能被MongoDB所接受。
MongoDB可以為前綴正則表達式(比如/^joey/)查詢創建索引,所以這種類型的查詢會非常高效。
正則表達式也可以匹配自身。雖然幾乎沒有人直接將正則表達式插入到數據庫中,但是萬一這么做了,也是可以用自身匹配的:
> db.foo.insert({"bar" : /baz/})
> db.foo.find({"bar" : /baz/})
查詢數組
數組絕大多數情況下可以這樣理解:每一個元素都是整個鍵的值。例如,如果數組是一個水果清單,比如下面這樣:
> db.food.insert({"fruit" : ["apple", "banana", "peach"]})
下面的查詢:
> db.food.find({"fruit" : "banana"})
會成功匹配該文檔。
①$all
通過多個元素來匹配數組,使用"$all"。
例如,假設創建包含3個元素的如下集合:
> db.food.insert({"_id" : 1, "fruit" : ["apple", "banana", "peach"]})
> db.food.insert({"_id" : 2, "fruit" : ["apple", "kumquat", "orange"]})
> db.food.insert({"_id" : 3, "fruit" : ["cherry", "banana", "apple"]})
要找到既有"apple"又有"banana"的文檔,就得用"$all"來查詢:
> db.food.find(fruit : {$all : ["apple", "banana"]})
db.food.insert({"_id" : 1, "fruit" : ["apple", "banana", "peach"]})
db.food.insert({"_id" : 3, "fruit" : ["cherry", "banana", "apple"]})
要是想查詢數組指定位置的元素,則需使用key.index語法指定下標,如:
> db.food.find({"fruit.2" : "peach"})
數組下標從0開始,上面的表達式會用數組的第3個元素和"peach"匹配。
②$size
"$size"用于查詢指定長度的數組。例:
> db.food.find({"fruit" : {"$size" : 3}})
③$slice操作符
find的第二個參數是可選的,可以指定返回那些鍵。"$slice"返回數組的一個子集合。
例如,假設現在有一個博客文章的文檔,要想反悔前10條評論,可以:
> db.blog.posts.findOne(criteria, {"comments" : {"$slice" : 10}})
也可以返回后10條評論,只要-10就可以了:
> db.blog.posts.findOne(criteria, {"comments" : {"$slice" : -10}})
"$slice"也可以接受偏移值和要返回的元素數量,來返回中間的結果:
> db.blog.posts.findOne(criteria, {"comments" : {"$slice" : [23, 10]}})
這個操作會跳過前23個元素,返回第24個~第33個元素。如果數組不夠33個元素,則返回第23個元素后面的所有元素。
使用"$slice"默認返回文檔中的所有鍵。
『查詢內嵌文檔』
有兩種方法查詢內嵌文檔:查詢整個文檔,或者只針對鍵/值對進行查詢。
例,對于如下文檔:
{
"name" : {
"first" : "Joe"
"last" : "Schmoe"
},
"age" : 45
}
要查詢姓名為Joe Schmoe的人可以這樣:
> db.people.find({"name" : {"first" : "Joe", "last" : "Schmoe"}})
可以使用點表示法查詢內嵌的鍵:
> db.people.find({"name.first" : {"first" : "Joe", "last" : "Schmoe"}})
例:假設有博客文章若干,要找到由Joe發表的5分以上的評論。要正確地指定一組條件,而不用指定每個鍵,要使用"$elemMatch"。這種模糊的命名條件句能用來部分指定匹配數組中的單個內嵌文檔的限定條件:
> db.blog.find({"comments" : {"$elemMatch" : {"author" : "joe", "score" : {"$gte" : 5}}}})
"$elemMatch"將限定條件進行分組,僅當需要對一個內嵌文檔的多個鍵操作時才會用到。
『$where查詢』
使用"$where"可以執行任意JavaScript作為查詢的一部分。
最典型的應用就是比較文檔中的兩個鍵的值是否相等。例如,有個條目列表,如果其中的兩個值相等則返回文檔。如下示例:
> db.foo.insert({"apple" : 1, "banana" : 6, "peach" : 3})
> db.foo.insert({"apple" : 8, "spinach" : 4, "watermelon" : 4})
第二個文檔中,"spinach"和"watermelon"的值相同,所以需要返回該文檔。MongoDB似乎用于不會提供一個$條件符來做這個,所以只能用"$where"自居借助JavaScript來完成:
> db.foo.find({"$where" : function() {
for(var current in this) {
for(var other in this) {
if(current != other && this[current] == this[other]) {
return true;}
}
}
return false;
}});
如果函數返回true,文檔就作為結果的一部分被返回;如果為false,則不然。
也可以用一個字符串來指定"$where"查詢。下面兩種表達式是完全等價的:
> db.foo.find({"$where" : "this.x + this.y == 10"})
> db.foo.find({"$where" : "function() { return this.x + this.y == 10; }"})
"$where"在速度上比常規查詢慢很多。
『游標』
要想從shell中創建一個游標,首先要對集合填充一些文檔,然后對其執行查詢,并將結果分配給一個局部變量(用var生命的變量就是局部變量)。這里,先創建一個簡單的幾何,而后做個查詢,并用cursor變量保存結果:
> for(i=0; i<100; i++) {
db.collection.insert({x : i});
}
> var cursor = db.collection.find();
要跌待結果,可以使用游標的next方法。也可以使用hasNext來查看有沒有其他結果。典型的結果遍歷如下:
> while (cursor.hasNext()) {
obj = cursor.next();
// do stuff
}
cursor.hasNext()檢查是否有后續結果存在,然后用cursor.next()將其獲得。
游標類還實現了迭代器接口,可以在foreach循環中使用。
> var cursor = db.people.find()
> cursor.forEach(function(x) {
print(x.name);
});
『limit、skip和sort』
要限制結果數量,可在find后使用limit函數。例如,只返回3個結果,可以這樣:
> db.c.find().limit(3)
忽略掉前3個匹配的文檔,然后返回余下的文檔:
> db.c.find().skip(3)
sort用一個對象作為參數:一組鍵/值對,鍵對應文檔的別名,值代表排序的方向。排序方向可以是1(升序)或者-1(降序)。如果指定了多個鍵,則按照多個鍵的順序逐個排序。例如,要按照"username"升序及"age"降序排序,可以這樣寫:
> db.c.find().sort({username : 1, age : -1})
這3個方法可以組合使用。這對于分頁非常有用。例如,你有個在線商店,有人想搜索mp3。若是想每頁返回50個結果,而且按照價格從高到低排序,可以這樣寫:
> db.stock.find({"desc" : "mp3"}).limit(50).sort({"price" : -1})
點擊“下一頁”可以看到更多的結果,通過skip也可以非常簡單地實現,只需要略過前50個結果就好了(已經在第一頁顯示了):
> db.stock.find({"desc" : "mp3"}).limit(50).skip(50).sort({"price" : -1})
比較順序:MongoDB處理不同類型的數據有一個順序:
(1)最小值
(2)null
(3)數字(整型、長整型、雙精度)
(4)字符串
(5)對象/文檔
(6)數組
(7)二進制數據
(8)對象ID
(9)布爾型
(10)日期型
(11)時間戳
(12)正則表達式
(13)最大值
『避免使用skip略過大量結果』
1.不用skip的結果進行分頁
最簡單的分頁方法是用limit返回結果的第一頁,然后將每個后續頁面作為相對于開始的偏移量返回。
> // do not use: slow for large skips
> var page1 = db.foo.find(criteria).limit(100)
> var page2 = db.foo.find(criteris).skip(100).limit(100)
> var page3 = db.foo.find(criteris).skip(200).limit(100)
...
然而,一般來講可以找到一種方法實現不用skip的分頁,這取決于查詢本身。例如,要按照"date"降序顯示文檔,可以用如下方式獲取結果的第一頁:
> var page1 = db.foo.find().sort({"date" : -1}).limit(100)
然后,可以利用最后一個文檔中"date"的值作為查詢條件,來獲取下一頁:
var latest = null;
// dispaly first page
while (page1.hasNext()) {
latest = page1.next();
display(latest);
}
// get next page
vat page2 = db.foo.find({"date" : {"$gt" : latest.date}});
page2.sort({"date" : -1}).limit(100);
這樣查詢中就沒有skip了。
2.隨機選取文檔
例:想隨機找一個加州的水暖工,可以對"profession"、"state"和"random"建立索引:
> db.people.ensureIndex({"profession" : 1, "state" : 1, "random" : 1})
這樣就能很快得出一個結果了。
『高級查詢選項』
查詢分為包裝的和普通的兩類。
普通的查詢:
> var cursor = db.foo.find({"foo" : "bar"})
有幾個選項用于包裝查詢。例如,假設我們執行一個排序:
> var cursor = db.foo.find({"foo" : "bar"}).sort({"x" : 1})
實際情況不是將{"foo" : "bar"}作為查詢直接發送給數據庫,而是將查詢包裝在一個更大的文檔中。shell會把查詢從{"foo" : "bar"}轉換成{"$query" : {"foo" : "bar"}}, "$orderby" : {"x" : 1}
絕大多數驅動程序有些輔助措施向查詢添加各種選項。舉例:
· $maxscan : integer
指定查詢最多掃描的文檔數量
· $min : document
查詢的開始條件
· $max : document
查詢的結束條件
· $hint : document
指定服務器使用哪個索引進行查詢
· $explain : boolean
獲取查詢執行的細節(用到的索引、結果數量、耗時等),而并非真正執行查詢。
· $snapshot : boolean
確保查詢的結果是在查詢執行那一刻的一致快照
獲取一致結果:當使用了"$snapshot"選項,查詢就是針對不變的集合視圖運行的。
游標內幕:客戶端游標以及客戶端游標表示的數據庫游標。
?