MongoDB查詢

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 確保查詢結果是在執行那一刻的一致快照
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內容