MongoDB的條件查詢


有段時間沒看書了,記錄下這個周末看《MongoDB權威指南》的筆記,目前看到是第四章:查詢

Java代碼    收藏代碼
  1. find({查詢條件限定},{返回字段})  

這是一個查詢的基本語法,各個簽名的作用已經說得很清楚。下面來細細展開:

當然最開始插入一批數據以供測試:

Python代碼    收藏代碼
  1. db.users.insert({"_id"1"name""aroba""age"22"friends"3})  
  2. db.users.insert({"_id"2"name""brob""age"23"friends"4})  
  3. db.users.insert({"_id"3"name""robin""age"24"friends"23})  
  4. db.users.insert({"_id"4"name""ccrob""age"25"friends"32})  
  5. db.users.insert({"_id"5"name""drobin""age"26"friends"15})  
  6. db.users.insert({"_id"6"name""rrobin""age""az""friends"19})  

 

1、 方法中的第一個參數:查詢條件限定是一個document結構,如為{}將默認返回所有數據

Python代碼    收藏代碼
  1. #查詢一個年齡為27的用戶:  
  2. db.users.find({"age":23})  
  3. #查詢一個年齡為27,姓名為robin的用戶,相當於AND  
  4. db.users.find({"age":23"name":"brob"})  

作為文檔的查詢條件,可以支持更復雜的格式:

Python代碼    收藏代碼
  1. #查詢年齡大於20且小於30的用戶  
  2. db.users.find({"age":{"$gt":20"$lte":23}})  

這里條件查詢有常用的:小於("$lt")、小於等於("$lte")、大於("$gt")、大於等於("$gte")、不等於("$ne") 。這些條件查詢對數字日期類型的字段比較適用

 

前面說到同時查詢age和name屬性,相當於AND查詢。這里來看看OR查詢,主要通過"$in"和"$or" 。對單一鍵有多個值與其匹配的話就用"$in",后面跟一個條件數組。

Python代碼    收藏代碼
  1. #查詢年齡在某個范圍的用戶:  
  2. db.users.find({"age":{"$in":[202225]}})  

"$in" 對支持的類型非常靈活,不同類型的條件可以同時查詢。與之相對應的就是"$nin",表示不在該范圍內的鍵

Python代碼    收藏代碼
  1. db.users.find({"age":{"$in":[202225"az"]}})  
  2. db.users.find({"age":{"$nin":[202225"az"]}})  

 與單一鍵的"$in"不同的是,"$or"是包含多個可能條件的數組。

Python代碼    收藏代碼
  1. #年齡在某個范圍內或者name在某個范圍內的用戶  
  2. db.users.find({"$or": [{"age"23 }, {"name""robin"}]})  
  3. db.users.find({"$or": [{"age": {"$in": [ 234"az" ]} }, {"name""robin"}]})  

 

2、返回字段

作為查詢的第二個參數,如果沒有的話是默認返回所有字段。可以對需要的返回字段指定:

Python代碼    收藏代碼
  1. db.users.find({}, {"name":1"age":1})  

1、這個查詢會返回name、age、_id字段
2、_id是默認返回,如果不要顯示加上("_id":0)     

Python代碼    收藏代碼
  1. db.users.find({}, {"name":1"age":1"_id":0})  

3、如果某個字段如age不存在,也不拋異常
4、需要顯示的字段設置為大於零的數就可以,但還是用1好理解,但如果對不需要顯示的字段且不是_id設置為0或其他會拋異常

Python代碼    收藏代碼
  1. db.users.find({}, {"name":1"age":0"_id":0})  

 這樣是不行的,如果不要返回age不加上就可以了

后面還將對數組查詢的返回字段做相應的說明,這里就先到此

 

3、幾點說明

1、"$not"元條件句,用在其他任何條件上,如

Python代碼    收藏代碼
  1. #age>20即查詢age小於等於20的用戶  
  2. db.user.find({"age": {"$not" : {"$gt" :20}}})    
  3. #這里查詢age不是1,6,11,16...等的用戶  
  4. db.user.find({"age": {"$not": {"$mod": [51]}}})   

 

2、條件查詢與更新修改器

Python代碼    收藏代碼
  1. #更新修改器  
  2. db.users.update({"age"23},{"$set":{"name":"zzzz"}})  
  3. #條件查詢  
  4. db.users.find({"age": {"$gt"20}})   

條件句是內層文檔鍵,修改器是外層文檔鍵。而且對同一個字段age來說可以是多個限定條件,但是修改器不能對應多個

 

3、null
如果某個字段的值為null,根據null來查詢時可以返回該條文檔,但也會返回不包含該字段的文檔

Python代碼    收藏代碼
  1. #新增兩條數據  
  2. >db.users.insert{ "_id" : 7"age" : 23"name" : "joe" }  
  3. >db.users.insert{ "_id" : 8"age" : 24"friends" : null, "name" : "sam" }  

 查詢鍵值為null的字段

Python代碼    收藏代碼
  1. >db.users.find({"friends": null})。  

 這里會返回friends為null的文檔,但是也會返回沒有該鍵的文檔

 

Shell代碼    收藏代碼
  1. "_id" : 7"age" : 23"name" : "joe" }  
  2. "_id" : 8"age" : 24"friends" : null, "name" : "sam" }  

 需要通過"$exists"來判定鍵值是否存在

Shell代碼    收藏代碼
  1. > db.users.find({"friends":{"$in":[null],"$exists":true}})  
  2. "_id" : 8"age" : 24"friends" : null, "name" : "rrbin" }  

 

4、正則表達式

Shell代碼    收藏代碼
  1. #這里會返回所有name中包含rob字段的文檔  
  2. > db.users.find({"name":/rob/})  
  3. #不僅對字段值進行正則匹配,如果值本身是正則式也匹配  

 

 

4、數組查詢

插入幾條數據測試

Python代碼    收藏代碼
  1. db.food.insert({"_id"1"fruit": ["apple""banana""peach"]})  
  2. db.food.insert({"_id"2"fruit": ["apple""orange"]})  
  3. db.food.insert({"_id"3"fruit": ["banana""peach""orange"]})  

以下是一些常用的查詢方法,直接上

Python代碼    收藏代碼
  1. #匹配fruit中包含banana的文檔  
  2. db.food.find({"fruit""banana"})  
  3. #必須匹配所有  
  4. db.food.find({"fruit": {"$all" : ["apple""peach"]}})  
  5. #精確匹配  
  6. db.food.find({"fruit": ["apple""orange"]})  
  7. #指定下標 key.index  
  8. db.food.find({"fruit.2""peach"})  
  9. #查詢指定長度的數組  
  10. db.food.find({"fruit": {"$size" : 3}})  

但是"$size"操作只能嚴格匹配,遇到比如要求數組大於或者小於之類的查詢就無能為力了。這里提供了解決的方案:對文檔新增size字段,每次對數組push或pop操作時,對size字段做相應的增減。查詢的時候再對字段size做相應的處理

Python代碼    收藏代碼
  1. db.food.update({"$push" :{"fruit" : "strawberry"} , "$inc" : {"size" : 1}})  
  2. db.food.find({"size" : {"$gt" : 3}})  

 

返回數組指定子集
"$slice"用於返回數組的一個子集,支持前、后或者偏移

Python代碼    收藏代碼
  1. db.food.insert({"_id"4"fruit": ["apple""banana""peach""orange""watermelon""lemon""cherry"]})  
  2. #取前2個  
  3. db.food.find({"_id":4}, {"fruit":{"$slice":2}})  
  4. #{u'_id': 4, u'fruit': [u'apple', u'banana']}  
  5. #取后兩個  
  6. db.food.find({"_id":4}, {"fruit":{"$slice":-2}})  
  7. #{u'_id': 4, u'fruit': [u'lemon', u'cherry']}  
  8. #從第2個開始取三個,這個其實達到分頁的效果,但書中明確指出對大量數據skip性能下降厲害,不建議考慮這種方式  
  9. db.food.find({"_id":4}, {"fruit":{"$slice":[23]}})  
  10. #{u'_id': 4, u'fruit': [u'peach', u'orange', u'watermelon']}  

 使用"$slice"獲取數組內的值時,其他的鍵也會默認返回,如果不需要返回非數組內的其他鍵這里可以指明,與前面返回不同的是這里可以用0

Python代碼    收藏代碼
  1. db.food.insert({"_id"5"sum":7"fruit": ["apple""banana""peach""orange""watermelon""lemon"]})  
  2. db.food.find({"_id":5}, {"fruit":{"$slice":[23]}, "_id":0"sum":0})  
  3. {u'fruit': [u'peach', u'orange', u'watermelon']}  

 

5、查詢內嵌文檔

這里主要考慮匹配查詢內嵌文檔,考慮如下文檔

Python代碼    收藏代碼
  1. db.users.insert({"_id"9"age":23"name": {"first":"joe""last":"sam"}})  
  2. db.users.insert({"_id"10"age":24"name": {"first":"joe""middle":"dd""last":"sam"}})  

查詢名字為joe sam的用戶

Python代碼    收藏代碼
  1. #查詢名字為joe sam的用戶  
  2. data = db.users.find({"name":{"first":"joe""last":"sam"}})  
  3. #返回第一條,實際上這相當於精確匹配,這個查詢條件將嚴格匹配順序、字段的數量。其實第二條也是我們想要的結果,那么正確的寫法應該是:  
  4. data = db.users.find({"name.first":"joe""name.last":"sam"})  

 書中說到一種復雜情況下的查詢:joe發表的5分以上的評論:

Python代碼    收藏代碼
  1. db.blog.insert({"_id":1"content":"....""comments":[{"name":"joe""score":3"comment":"nice"}, {"name":"sam""score":5"comment":"zzz"}, {"name":"joe""score":5"comment":"good"}]})  
  2. data = db.blog.find({"comments":{"name":"joe""score":{"$gte":5}}})  
  3. #這樣是查不到數據的,內嵌文檔要求匹配整個文檔,而不是comments鍵  
  4. data = db.blog.find({"comments.name":"joe""comments.score":{"$gte":5}})  
  5. #這個查詢會返回這條記錄,其實是匹配的commets中各個鍵,即joe匹配第一條,score匹配第二條  
  6. data = db.blog.find({"comments":{"$elemMatch":{"name":"joe""score":{"$gte":5}}}})  

 

 

6、分頁

分頁在前面說到"$slice"時,其實是達到分頁的效果,前面也說了弊端,這里進一步說明。這里采用limit限制返回結果,slice跳過指定數量文檔,sort對查詢結果排序

limit
db.users.find().limit(3)結果集超過三條返回三條,不足返回實際數量,貌似對負數不感冒,比如-2還是返回前兩條,或者limit里沒有偏移這個概念
skip
db.users.find().limit(3)省略結果集前三個,返回剩下的,結果集不足三個就啥都木有了,當然這個也一樣,別寫負數了,否則拋異常
sort
對結果集排序:1升序,-1降序。可支持多個鍵/對

Python代碼    收藏代碼
  1. db.users.find().sort([("name"1), ("age", -1)])  
Shell代碼    收藏代碼
  1. db.users.find().sort({"name":1,"age":-1})  
Python代碼    收藏代碼
  1. #這就是分頁  
  2. db.users.find().limit(2).sort("_id"1)  
  3. db.users.find().limit(2).skip(2).sort("_id"1)  
  4. db.users.find().limit(2).skip(4).sort("_id"1)  

這對大數據量的skip性能影響較大,這里也提供了一些繞過的方法。比如先取得最后一條的記錄的某個唯一鍵,再查詢大於該鍵的值。可以看出這個限制條件挺多,當然容易想到的采用主鍵"_id",這是主鍵必須是數字了

 

 

書中還說到了其他的一些高級特性,如$where、隨機獲取、包裝查詢、獲取一致性結果等,這里就沒記錄下來


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM