find
查詢即返回一個集合中文檔的子集,子集范圍從0到整個文檔。
find
第一個參數決定要返回那些文檔,其形式也是一個文檔,說明要執行的查詢細節??盏牟樵兾臋n {}
會匹配集合的全部內容,要是不指定查詢文檔,默認為 {}
。
# 查詢users集合中所有內容
db.users.find()
向查詢文檔中添加鍵值對時,意味著限制查詢條件。
db.users.find({name:'alice'});
db.users.find({age:20});
# 查詢名為alice且年齡為20的用戶
db.users.find({name:'alice', age:20});
返回值
查詢結果并非每次都需全部返回,可通過find的第二個參數指定需要的鍵。
db.users.find({}, {name:1, email:1})
限制
查詢使用上是有些限制的,數據庫所關心的查詢的值必須是常量,即不能引用文檔中其他鍵值。
# 存貨表(stocks)設計時,initial表示初始化存貨量,current表示當前存貨量。購買行為發生時,current減少即可。
# 查詢脫銷的產品
db.stocks.find({current:0})
范圍查詢
$lt
、$lte
、$gt
、$gte
分別對應 <
、<=
、>
、>=
,可將其組合以便于查詢一個范圍的值。
# 查詢年齡在18到30歲的用戶
db.users.find({ age:{$gte:18, $lte:30} })
# 查詢在2017-01-01前注冊的用戶
db.users.find({ registred_at: {$lt: new Date('2017/01/01') } });
對于文檔鍵值不等于某個特定值得情況,可使用$ne
不等于條件操作符,$ne
可用于所有類型的數據。
# 查詢名字不為 `joe` 的用戶
db.users.find({ name:{$ne:'joe'} })
或查詢
MongoDB有兩種方式進行或查詢,$in
可用來查詢一個鍵的多個值,$or
更為通用可完成多個鍵值的任意給定值。對于單個鍵要有多個值與其匹配的話,使用$in
加一個條件數組。
db.users.find(name:{$in:['alice','ben','carl']});
與 $in
相反的是 $nin
,返回與數組中條件不匹配的文檔。
db.users.find({name:{$nin:['junchow']}})
$in
能對單個鍵做或查詢,要實現多條件的或查詢應使用 $or
。$or
接受一個包含所有可能條件的數組作為參數。
db.users.find({$or:[{name:'junchow'}, {gender:'female'}]})
$or
可含有其他條件
db.users.find({$or:[{name:'junchow', tags:{$in:['happy','handsome']}}]})
非查詢
$not
是元條件句,可用在任何其他條件之上。
# 查詢出sort為1、6、11、16...的用戶
db.users.find({sort:{$mod:[5,1]}})
db.users.find({sort:{$not:{$mod:[5,1]}}})
條件規則
根據$開頭的鍵處在不同的位置,可斷定條件句是內層文檔的鍵,修改器則是外層文檔的鍵。
# 可對一個鍵應用多個條件
db.users.find({age:{$gt:18, $lt:30}})
#一個鍵不能對應多個更新修改器
db.users.find({$inc:{age:1}, $set:{age:40}});//error
特定類型的查詢
null能匹配自身
# null不僅匹配自身還匹配不存在的,會返回缺少這個鍵的所有文檔。
db.users.find({name:null})
# 若僅匹配鍵值為null的文檔,即要檢查鍵值是否為null,還需通過$exists條件判斷鍵值已存在。由于沒有$eq操作符,形式上會有些費解。
db.users.find({name:{$in:[null], $exists:true}})
正則
正則能靈活有效地匹配字符串
# 查詢忽略大小寫名為joe的文檔
db.users.find({name:/joe/i})
# 查詢匹配大小寫組合形式的joe
db.users.find({name:/joe?/i})
MongoDB使用Perl兼容的正則(PCRE)庫來匹配正則,PCRE支持的正則語法都能被MongoDB所接受。建議在查詢中使用正則前,先在JS Shell中檢查一下語法,確保匹配與設想的一致。
MongoDB可為前綴型正則查詢創建索引,所以此類查詢非常高效。
db.users.find({name:/^joey/})
查詢數組
每個元素都是整個鍵的值
db.users.insert({tags:['ios', 'wins', 'linux']})
db.users.find({tags:'linux'})
$all
通過多個元素來匹配數組
# 查詢即有ios又有linux的文檔
db.users.find({tags:{$all:['wins', 'ios']}})
# 查詢數組指定位置的元素可使用 key.index 指定下標,數組下標以0開始。
db.users.find({'tags.1': 'wins'})
# 查詢指定長度的數組,$size不能與其他子句組合。
db.users.find({tags:{$size:3}})
#當查詢需一個長度范圍時可通過在文檔中添加一個size鍵的方式來實現,不幸的是這種技巧并不能與 $addToSet同時使用。
db.users.update({$push:{remark:''}, $inc:{size:1}})
db.users.find({size:{$gt:3}})
$slice
操作符返回文檔中指定數組的內部值
# 查詢博客某文章前10條評論
db.blogs.findOne(criteria, {comments:{$slice:10}})
# 查詢博客某文章后10條評論
db.blogs.findOne(criteria, {comments:{$slice:-10}})
# 查詢中junchow書架中第2到第4本書
db.users.find({name:'junchow'}, {books:{$slice:[1,3]}, _id:0})
# 查詢出最后一本書
db.users.find({name:'junchow'}, {books:{$slice:-1}, _id:0})
查詢內嵌文檔
db.users.insert({name:'tom', fullname:{first:'joe', last:'schmoe'}})
# 使用點標識符查詢內嵌的鍵,查詢文檔可包含點來表達深入內嵌文檔內部的意思。
db.users.find({'fullname.first':'joe'})
為jim添加簡歷文檔 jim.json
db.users.insert({name:'jim'});
var scores = [
{subject:'php', score:'A'},
{subject:'database', score:'B'},
{subject:'javascript', score:'A+'}
];
db.users.update({name:'jim'}, {$set:{scores:scores}});
# 查詢出考過php的用戶
db.users.find({'scores.subject':'php'},{_id:0});
查詢博客文章中由junchow發表的5分以上的評論
# elemMatch 將限定條件進行分組,僅當需對內嵌文檔的多個鍵操作時才會用到
db.blogs.find({comments:{$elemMatch:{author:'junchow', score:{$gte:5}}}})
$where查詢
鍵值對是很有表現力的查詢方式,但依然有些需求是它無法滿足的,此時就需要$where子句了。
一定要避免使用$where查詢,因為在速度上要比常規查詢慢,由于每個文檔都要從BSON轉換成JSON,然后通過$where表達式來運行,同樣不能利用索引。所以只能在走投無路時才考慮使用 $where。
分頁
limit() 返回指定的數據條數,limit指定的是上限而非下限。
# 查詢出前5條數據
db.users.find({}, {_id:0, name:1}).limit(5)
skip() 返回指定數據的跨度或偏移量
由于skip存在性能上的問題,為解決此問題可在集合中加入date字段,在每次查詢時將上次最后一個文檔的日期記錄下來,下次查詢時可使用date為條件。
db.users.find({date:{$gt:日期數據}}).limit(5)
建議是將軟件的重點放在便捷和精確查詢上,而不是分頁的性能上。
# 查詢第5到10條的文檔
db.users.find({}, {_id:0, name:1}).limit(5).skip(5)
排序
sort 返回按條件字段排序的數據,1表示升序,-1表示降序。
db.users.find({}, {_id:0, name:1, age:1}).limit(5).skip(5).sort({age:1})
db.users.find({}, {_id:0, name:1, age:1}).limit(5).skip(5).sort({age:-1})
MongoDB中key可存不同類型的數據,因此排序有存在優先級的問題。
隨機
從集中中隨機挑選文檔,最笨也是最慢的方式是先計算文檔總數,然后選擇一個從0到總數之間的隨機數,再利用find()查詢。
var count = db.users.count();
var random = Math.floor(Math.random()*count);
db.users.find().skip(random).limit(1);
解決方案是從在插入文檔時給每個文檔添加額外的隨機鍵。
db.users.insert({name:'john', random:Math.random()})
查詢隨機文檔時僅需計算隨機數并將其作為查詢條件即可
db.users.findOne({random:{$gt:Math.random()}})
游標
數據庫使用游標返回find查詢結果,客戶端對游標的實現通常能對最終結果進行有效的控制。可限制結果的數量,略過部分結果,根據任意方向任意鍵的組合對結果進行各種排序,或是執行其他一些功能強大的操作。
要想從shell中創建一個游標,首先要對集合填充文檔然后對其查詢,并將結果分配給一個局部變量。
for(i=0; i<1000; i++){
db.test.insert({x:i})
}
var cursor = db.test.find()
游標的好處是一次可查看一條結果,若將結果全部放在全局變量中,MongoDB shell會自動迭代,自動顯示最開始的若干文檔。
要迭代結果可使用游標的next(),也可使用hasNext()來查看是否有結果。
while(cursor.hasNext()){
obj = cursor.next();
//do stuff
}
游標類實現了迭代器接口,所以可在 foreach 循環中使用。
var cursor = db.test.find();
cursor.forEach(function(item){
print(item.name);
});
當調用find()時,shell并不立即查詢數據庫,而是等待真正開始要求獲取結果的時候才發送查詢,這樣在執行之前給查詢額外的選項。幾乎所有游標對象的方法都返回游標本身,這樣可按任意順序組成方法鏈。
游標的銷毀條件
- 客戶端發來信息讓其銷毀
- 游標迭代完畢
- 默認游標超過10分鐘自動清除
查詢快照
快照后會針對不變的集合進行游標運動
db.users.find({$query:{name:'jim'}, $snapshot:true})
高級查詢
查詢分為包裝的和普通的兩類。
# 普通查詢
db.users.find({name:'junchow'})
# 包裝查詢
db.users.find({age:18}).sort(age:1)
# 包裝查詢的本質
db.users.find($query:{age:18}, $orderby:{age:1})
包裝查詢選項
- $maxscan:integer 指定查詢最多掃描的文檔數量
- $min:document 查詢的開始條件
- $max:document 查詢的結束條件
- $hint:document 指定服務器使用哪個索引進行查詢
- $explain:boolean 獲取查詢執行細節
- $snapshot:boolean 確保查詢結果是在執行那一刻的一致快照