1. 前言 在這篇博文中,我們將學習如何查詢mongoDB中的數據。當我們把數據存儲在mongoDB以後,我們需要把數據查詢出來。畢竟CRUD操作中,查詢操作在我們系統中是我們應用比較頻繁的操作。我們需要應對不同的業務需求,構造合適的查詢條件去查詢我們想要的數據。我們需要去學習mongoDB給我們提 ...
1. 前言
在這篇博文中,我們將學習如何查詢mongoDB中的數據。當我們把數據存儲在mongoDB以後,我們需要把數據查詢出來。畢竟CRUD操作中,查詢操作在我們系統中是我們應用比較頻繁的操作。我們需要應對不同的業務需求,構造合適的查詢條件去查詢我們想要的數據。我們需要去學習mongoDB給我們提供了哪些查詢相關的語法和功能。在這裡,我們使用mongodb自帶的mongo shell(mongo shell是一個javascript環境的mongodb客戶端,支持js語法)來學習。
2. 準備
在開始之前,我們需要準備一下實驗用的數據:
// 啟動mongo shell客戶端 $ mongo // 在這裡我們使用test資料庫,如果沒有這個資料庫,會自動創建 > use test // 在users collection中插入6條用戶數據 > db.users.insertMany( [ { _id: 1, name: "sue", age: 19, type: 1, status: "P", favorites: { artist: "Picasso", food: "pizza" }, finished: [ 17, 3 ], badges: [ "blue", "black" ], points: [ { points: 85, bonus: 20 }, { points: 85, bonus: 10 } ] }, { _id: 2, name: "bob", age: 42, type: 1, status: "A", favorites: { artist: "Miro", food: "meringue" }, finished: [ 11, 25 ], badges: [ "green" ], points: [ { points: 85, bonus: 20 }, { points: 64, bonus: 12 } ] }, { _id: 3, name: "ahn", age: 22, type: 2, status: "A", favorites: { artist: "Cassatt", food: "cake" }, finished: [ 6 ], badges: [ "blue", "red" ], points: [ { points: 81, bonus: 8 }, { points: 55, bonus: 20 } ] }, { _id: 4, name: "xi", age: 34, type: 2, status: "D", favorites: { artist: "Chagall", food: "chocolate" }, finished: [ 5, 11 ], badges: [ "red", "black" ], points: [ { points: 53, bonus: 15 }, { points: 51, bonus: 15 } ] }, { _id: 5, name: "xyz", age: 23, type: 2, status: "D", favorites: { artist: "Noguchi", food: "nougat" }, finished: [ 14, 6 ], badges: [ "orange" ], points: [ { points: 71, bonus: 20 } ] }, { _id: 6, name: "abc", age: 43, type: 1, status: "A", favorites: { food: "pizza", artist: "Picasso" }, finished: [ 18, 12 ], badges: [ "black", "blue" ], points: [ { points: 78, bonus: 8 }, { points: 57, bonus: 7 } ] } ] )View Code
3. 基本查詢
MongoDB提供了db.collection.find()方法來執行查詢操作。find方法接受兩個參數:一個查詢條件,一個是投影的欄位。這兩個參數都不是必須的,如果省略了查詢條件,則預設列出collection中的所有文檔。
db.users.find() // 這個和上面的語句是等價的 db.users.find({})
3.1 等值查詢
通過find()方法來執行等值查詢的時候,可以通過{<field>:<value>}的方式來指定查詢條件,這個條件表示在collection中查詢滿足field的值為value的所有文檔。假設我們需要查找所有status是'A'的用戶,我們可以這麼查詢:
db.users.find({status: 'A'})
查詢結果
{ "_id" : 3, "name" : "ahn", "age" : 22, "type" : 2, "status" : "A", "favorites" : { "artist" : "Cassatt", "food" : "cake" }, "finished" : [ 6 ], "badges" : [ "blue", "red" ], "points" : [ { "points" : 81, "bonus" : 8 }, { "points" : 55, "bonus" : 20 } ] }
{ "_id" : 6, "name" : "abc", "age" : 43, "type" : 1, "status" : "A", "favorites" : { "food" : "pizza", "artist" : "Picasso" }, "finished" : [ 18, 12 ], "badges" : [ "black", "blue" ], "points" : [ { "points" : 78, "bonus" : 8 }, { "points" : 57, "bonus" : 7 } ] }
3.2 操作符查詢
mongodb支持使用操作符來構造查詢條件,比如比較操作符,如$gt,$lt等。使用操作符的查詢條件通過{<field>: {<operator>:<value>}}來表示。假設我們要查詢age超過22的用戶,可以這麼做:
db.users.find({age: {$gt: 22}})
查詢結果
{ "_id" : 2, "name" : "bob", "age" : 42, "type" : 1, "status" : "A", "favorites" : { "artist" : "Miro", "food" : "meringue" }, "finished" : [ 11, 25 ], "badges" : [ "green" ], "points" : [ { "points" : 85, "bonus" : 20 }, { "points" : 64, "bonus" : 12 } ] } { "_id" : 4, "name" : "xi", "age" : 34, "type" : 2, "status" : "D", "favorites" : { "artist" : "Chagall", "food" : "chocolate" }, "finished" : [ 5, 11 ], "badges" : [ "red", "black" ], "points" : [ { "points" : 53, "bonus" : 15 }, { "points" : 51, "bonus" : 15 } ] } { "_id" : 5, "name" : "xyz", "age" : 23, "type" : 2, "status" : "D", "favorites" : { "artist" : "Noguchi", "food" : "nougat" }, "finished" : [ 14, 6 ], "badges" : [ "orange" ], "points" : [ { "points" : 71, "bonus" : 20 } ] } { "_id" : 6, "name" : "abc", "age" : 43, "type" : 1, "status" : "A", "favorites" : { "food" : "pizza", "artist" : "Picasso" }, "finished" : [ 18, 12 ], "badges" : [ "black", "blue" ], "points" : [ { "points" : 78, "bonus" : 8 }, { "points" : 57, "bonus" : 7 } ] }
3.3 and關係
mongodb支持的and關係查詢很簡單,當一個查詢條件中包含多個<field:value>對的時候,這些條件之間的關係就是and關係。所以and關係的查詢可以這樣表示{<field1>:<value1>, <field2>:<value2>, ...,<fieldN>:<valueN>}。假設我們要查詢滿足條件:status是'D',並且age大於23的用戶,我們可以這麼查詢:
db.users.find({status: 'D', age: {$gt: 23}})
查詢結果
"_id" : 4, "name" : "xi", "age" : 34, "type" : 2, "status" : "D", "favorites" : { "artist" : "Chagall", "food" : "chocolate" }, "finished" : [ 5, 11 ], "badges" : [ "red", "black" ], "points" : [ { "points" : 53, "bonus" : 15 }, { "points" : 51, "bonus" : 15 } ] }
3.4 or關係
mongodb通過操作符"$or"來支持or關係的查詢。or關係的查詢條件可以這麼構造:{$or: [{<field1>:<value1>}, {<field2>:<value2>},..., {<fieldN>:<valueN>}]}。比如我們想查詢status是'A' 或者age大於23的用戶,我們可以這麼做:
db.users.find({$or: [{status: 'A'}, {age: {$gt: 23}}]})
查詢條件
{ "_id" : 2, "name" : "bob", "age" : 42, "type" : 1, "status" : "A", "favorites" : { "artist" : "Miro", "food" : "meringue" }, "finished" : [ 11, 25 ], "badges" : [ "green" ], "points" : [ { "points" : 85, "bonus" : 20 }, { "points" : 64, "bonus" : 12 } ] } { "_id" : 3, "name" : "ahn", "age" : 22, "type" : 2, "status" : "A", "favorites" : { "artist" : "Cassatt", "food" : "cake" }, "finished" : [ 6 ], "badges" : [ "blue", "red" ], "points" : [ { "points" : 81, "bonus" : 8 }, { "points" : 55, "bonus" : 20 } ] } { "_id" : 4, "name" : "xi", "age" : 34, "type" : 2, "status" : "D", "favorites" : { "artist" : "Chagall", "food" : "chocolate" }, "finished" : [ 5, 11 ], "badges" : [ "red", "black" ], "points" : [ { "points" : 53, "bonus" : 15 }, { "points" : 51, "bonus" : 15 } ] } { "_id" : 6, "name" : "abc", "age" : 43, "type" : 1, "status" : "A", "favorites" : { "food" : "pizza", "artist" : "Picasso" }, "finished" : [ 18, 12 ], "badges" : [ "black", "blue" ], "points" : [ { "points" : 78, "bonus" : 8 }, { "points" : 57, "bonus" : 7 } ] }
4. 嵌套文檔查詢
如果在文檔中的一個欄位上的值也是一個文檔,當我們需要查詢這個文檔中的子文檔中的值得時候,就涉及到嵌套文檔的查詢。MongoDB支持對嵌套文檔的查詢。
4.1 匹配整個子文檔
匹配整個子文檔可以認為也是一種等值查詢,只不過這次的值是一個子文檔。所以查詢條件就是這樣的{<field>:<sub document>}。假設我們要查詢favorites這個欄位上,artist為'Picasso',food為'pizza'的用戶,我們可以這樣做:
db.users.find( { favorites: { artist: "Picasso", food: "pizza" } } )
查詢結果
{ "_id" : 1, "name" : "sue", "age" : 19, "type" : 1, "status" : "P", "favorites" : { "artist" : "Picasso", "food" : "pizza" }, "finished" : [ 17, 3 ], "badges" : [ "blue", "black" ], "points" : [ { "points" : 85, "bonus" : 20 }, { "points" : 85, "bonus" : 10 } ] }
4.2 匹配子文檔中的欄位
mongodb支持匹配子文檔中的欄位,通過點(.)符號來表示子文檔中的欄位,比如我們在favorites欄位對應的子文檔中的artist欄位上做查詢操作,可以通過favorites.artist來表示這個子文檔中的artist欄位
db.users.find( { "favorites.artist": "Picasso" } )
查詢結果
{ "_id" : 1, "name" : "sue", "age" : 19, "type" : 1, "status" : "P", "favorites" : { "artist" : "Picasso", "food" : "pizza" }, "finished" : [ 17, 3 ], "badges" : [ "blue", "black" ], "points" : [ { "points" : 85, "bonus" : 20 }, { "points" : 85, "bonus" : 10 } ] }
{ "_id" : 6, "name" : "abc", "age" : 43, "type" : 1, "status" : "A", "favorites" : { "food" : "pizza", "artist" : "Picasso" }, "finished" : [ 18, 12 ], "badges" : [ "black", "blue" ], "points" : [ { "points" : 78, "bonus" : 8 }, { "points" : 57, "bonus" : 7 } ] }
5. 在數組上查詢
如果文檔中的欄位的值是數組類型,mongodb支持對數組類型的欄位構造查詢條件。
5.1 匹配整個數組
mongodb對整個數組進行匹配的時候,和匹配整個嵌套文檔的方式類似。只要在條件中給出整個數組就可以了,就像這樣{<field>:<value>},只不過這裡的<value>是需要匹配的整個數組。假設我們需要查找badges的值為"['blue', 'black']"的用戶,我們可以這樣做
db.users.find( { badges: [ "blue", "black" ] } )
查詢結果
{ "_id" : 1, "name" : "sue", "age" : 19, "type" : 1, "status" : "P", "favorites" : { "artist" : "Picasso", "food" : "pizza" }, "finished" : [ 17, 3 ], "badges" : [ "blue", "black" ], "points" : [ { "points" : 85, "bonus" : 20 }, { "points" : 85, "bonus" : 10 } ] }
5.2 查找數組中的一個元素
除了對數組類型的欄位做完全匹配的查詢,我們也可以把數組的元素作為查詢條件,主要數組中包含了這個元素,那麼都會被匹配到。比如我們需要查找badges的數組中包含了'black'元素的所有用戶,我們可以這樣做
db.users.find( { badges: "black" } )
查詢結果
{ "_id" : 1, "name" : "sue", "age" : 19, "type" : 1, "status" : "P", "favorites" : { "artist" : "Picasso", "food" : "pizza" }, "finished" : [ 17, 3 ], "badges" : [ "blue", "black" ], "points" : [ { "points" : 85, "bonus" : 20 }, { "points" : 85, "bonus" : 10 } ] } { "_id" : 4, "name" : "xi", "age" : 34, "type" : 2, "status" : "D", "favorites" : { "artist" : "Chagall", "food" : "chocolate" }, "finished" : [ 5, 11 ], "badges" : [ "red", "black" ], "points" : [ { "points" : 53, "bonus" : 15 }, { "points" : 51, "bonus" : 15 } ] } { "_id" : 6, "name" : "abc", "age" : 43, "type" : 1, "status" : "A", "favorites" : { "food" : "pizza", "artist" : "Picasso" }, "finished" : [ 18, 12 ], "badges" : [ "black", "blue" ], "points" : [ { "points" : 78, "bonus" : 8 }, { "points" : 57, "bonus" : 7 } ] }
可以看到,這三個用戶的badges欄位中的值中,都有‘black’元素在數組中。
5.3 匹配數組中指定下標的元素
mongodb支持把數組中指定下標的元素作為查詢條件來構造查詢語句,通過類似於引用子文檔的點號(.)方式來說引用指定下標的元素。比如badges欄位中數組的第一個元素,可以表示成:badges.0,類似的,第N個就是badges.N。假設我們想查找滿足badges數組中的第一個元素是'black'的用戶,我們可以這樣做:
db.users.find( { "badges.0": "black" } )
查詢結果
{ "_id" : 6, "name" : "abc", "age" : 43, "type" : 1, "status" : "A", "favorites" : { "food" : "pizza", "artist" : "Picasso" }, "finished" : [ 18, 12 ], "badges" : [ "black", "blue" ], "points" : [ { "points" : 78, "bonus" : 8 }, { "points" : 57, "bonus" : 7 } ] }
查詢結果看到,只有一個滿足條件的用戶。雖然別的用戶的badges欄位也有'black'欄位,但是由於需要查找數組的第一個元素是'black'的用戶,所以只有一個用戶是滿足條件的。
5.4 對數組元素指定多個查詢條件
多個條件之間的and關係
mongodb提供了一個"$elemMatch"操作符,這個操作符的作用是對數組中的元素進行多條件匹配,只要數組中至少一個元素滿足指定的條件,那麼就表示匹配成功,也就是說,'$elemMatch'操作符指定的條件之間是"與"的關係。來看例子,假設我們要找到滿足finished欄位中的數組元素的值大於15,小於20,我們可以這樣做:
db.users.find( { finished: { $elemMatch: { $gt: 15, $lt: 20 } } } )
查詢結果
{ "_id" : 1, "name" : "sue", "age" : 19, "type" : 1, "status" : "P", "favorites" : { "artist" : "Picasso", "food" : "pizza" }, "finished" : [ 17, 3 ], "badges" : [ "blue", "black" ], "points" : [ { "points" : 85, "bonus" : 20 }, { "points" : 85, "bonus" : 10 } ] }
{ "_id" : 6, "name" : "abc", "age" : 43, "type" : 1, "status" : "A", "favorites" : { "food" : "pizza", "artist" : "Picasso" }, "finished" : [ 18, 12 ], "badges" : [ "black", "blue" ], "points" : [ { "points" : 78, "bonus" : 8 }, { "points" : 57, "bonus" : 7 } ] }
我們可以看到,“$elemMatch”操作符的用法,是用指定的多個條件來匹配數組中的每一個值,只要數組中至少有一個值滿足我們指定的條件,就表示匹配成功。
多條件之間的or關係
mongodb在數組匹配的時候,可以返回滿足指定條件的結果的並集。通俗的講,指定的條件之間是“或”的關係,即只要數組中的任何一個元素滿足多個查詢條件中的任何一個,那麼就認為這個文檔被匹配上了。比如,我們如果這樣指定查詢條件
db.users.find( { finished: { $gt: 15, $lt: 20 } } )
我們先來看下查詢的結果
{ "_id" : 1, "name" : "sue", "age" : 19, "type" : 1, "status" : "P", "favorites" : { "artist" : "Picasso", "food" : "pizza" }, "finished" : [ 17, 3 ], "badges" : [ "blue", "black" ], "points" : [ { "points" : 85, "bonus" : 20 }, { "points" : 85, "bonus" : 10 } ] } { "_id" : 2, "name" : "bob", "age" : 42, "type" : 1, "status" : "A", "favorites" : { "artist" : "Miro", "food" : "meringue" }, "finished" : [ 11, 25 ], "badges" : [ "green" ], "points" : [ { "points" : 85, "bonus" : 20 }, { "points" : 64, "bonus" : 12 } ] } { "_id" : 6, "name" : "abc", "age" : 43, "type" : 1, "status" : "A", "favorites" : { "food" : "pizza", "artist" : "Picasso" }, "finished" : [ 18, 12 ], "badges" : [ "black", "blue" ], "points" : [ { "points" : 78, "bonus" : 8 }, { "points" : 57, "bonus" : 7 } ] }
這個查詢條件和上面的元素匹配的查詢條件相比,查詢的結果我們看到,中間的記錄中,finished的值是[11, 25]的也滿足查詢條件。這是因為{finished: {$gt: 15, $lt: 20}}這個條件,表示數組中的任何一個元素,只要滿足大於15或者小於20,都被認為是滿足查詢條件的,指定的條件之間是“或”的關係。而通過"$elemMatch"操作符指定的查詢條件,條件之間的關係是“與”的關係。所以不難理解,值為[11, 25]的那個記錄之所以被匹配,是因為它滿足25是大於條件中的15的,而11是小於條件中的20的,所以自然就滿足條件了。
5.5 數組中包含子文檔的查詢
當數組中包含子文檔的時候,也可以為這些子文檔中的欄位構造查詢條件。
使用數組下標定位到具體的子文檔
這種方式的查詢條件,是同時利用數組的下標表示和文檔中欄位的點號(.)表示法來指定數組中子文檔中的欄位。講的有點繞,我們來看具體的例子。
db.users.find( { 'points.0.points': { $lte: 55 } } )
這條查詢語句的意思,表示我們要查詢points欄位中,它包含的數組中下標為0的子文檔中的points欄位的值,滿足條件{$lte: 55},也就是這個子文檔中的points欄位的值小於等於55。查詢結果如下:
{"_id" : 4, "name" : "xi", "age" : 34, "type" : 2, "status" : "D", "favorites" : { "artist" : "Chagall", "food" : "chocolate" }, "finished" : [ 5, 11 ], "badges" : [ "red", "black" ], "points" : [ { "points" : 53, "bonus" : 15 }, { "points" : 51, "bonus" : 15 } ] }
匹配任何一個滿足條件的子文檔
如果我們省略了數組的下標,那麼查詢條件就變成了這樣
db.users.find( { 'points.points': { $lte: 55 } } )
它表示查詢points數組中任何一個子文檔,只要子文檔中的points欄位的值滿足條件{$lte: 55}就可以了。
查詢結果:
{ "_id" : 3, "name" : "ahn", "age" : 22, "type" : 2, "status" : "A", "favorites" : { "artist" : "Cassatt", "food" : "cake" }, "finished" : [ 6 ], "badges" : [ "blue", "red" ], "points" : [ { "points" : 81, "bonus" : 8 }, { "points" : 55, "bonus" : 20 } ] }
{ "_id" : 4, "name" : "xi", "age" : 34, "type" : 2, "status" : "D", "favorites" : { "artist" : "Chagall", "food" : "chocolate" }, "finished" : [ 5, 11 ], "badges" : [ "red", "black" ], "points" : [ { "points" : 53, "bonus" : 15 }, { "points" : 51, "bonus" : 15 } ] }
可以看到,查詢結果中包含了所有滿足條件的記錄。
多個條件之間的and關係
利用上面提到的數組的多條件“and”關係匹配的方法,也可以用來為數組中的子文檔指定多個匹配條件。查詢條件也是利用了"$elemMatch"操作符:
db.users.find( { points: { $elemMatch: { points: { $lte: 70 }, bonus: 20 } } } )
可以看到,我們在對points數組中的子文檔指定多個匹配匹配條件的時候,和上面提到的對數組中元素指定多個匹配條件的方式類似。只不過,上面是對整數指定匹配條件,這裡是換成了對子文檔指定匹配條件,而且子文檔中的欄位可以直接引用,不用採用點(.)號的方式引用。
查詢結果
{ "_id" : 3, "name" : "ahn", "age" : 22, "type" : 2, "status" : "A", "favorites" : { "artist" : "Cassatt", "food" : "cake" }, "finished" : [ 6 ], "badges" : [ "blue", "red" ], "points" : [ { "points" : 81, "bonus" : 8 }, { "points" : 55, "bonus" : 20 } ] }
多個條件之間的or關係
和對數組中元素指定多個or關係的查詢條件一樣,對數組中的子文檔指定多個or關係的查詢條件的語句,在形式上類似。來看具體的例子:
db.users.find( { "points.points": { $lte: 70 }, "points.bonus": 20 } )
這裡使用點(.)號來引用數組中的子文檔的欄位,其中兩個條件之間是“或”的關係,語句的意思大體是這樣的:查詢滿足points數組中,任何一個子文檔的points欄位的值不小於70或者任何一個子文檔的bonus欄位的值為20的記錄。查詢結果是這樣的:
{ "_id" : 2, "name" : "bob", "age" : 42, "type" : 1, "status" : "A", "favorites" : { "artist" : "Miro", "food" : "meringue" }, "finished" : [ 11, 25 ], "badges" : [ "green" ], "points" : [ { "points" : 85, "bonus" : 20 }, { "points" : 64, "bonus" : 12 } ] }
{ "_id" : 3, "name" : "ahn", "age" : 22, "type" : 2, "status" : "A", "favorites" : { "artist" : "Cassatt", "food" : "cake" }, "finished" : [ 6 ], "badges" : [ "blue", "red" ], "points" : [ { "points" : 81, "bonus" : 8 }, { "points" : 55, "bonus" : 20 } ] }
如果我們對數組的下標做了指定,那麼可以在上面的多個“or”條件之上,再加一個數組元素的位置限定。比如:
db.users.find({'points.0.points': {$gte: 70}, 'points.0.bonus': 8})
這裡使用了上面提到的,在數組中指定下標的方式來指定數組中的某一個元素。
查詢結果
{ "_id" : 3, "name" : "ahn", "age" : 22, "type" : 2, "status" : "A", "favorites" : { "artist" : "Cassatt", "food" : "cake" }, "finished" : [ 6 ], "badges" : [ "blue", "red" ], "points" : [ { "points" : 81, "bonus" : 8 }, { "points" : 55, "bonus" : 20 } ] }
{ "_id" : 6, "name" : "abc", "age" : 43, "type" : 1, "status" : "A", "favorites" : { "food" : "pizza", "artist" : "Picasso" }, "finished" : [ 18, 12 ], "badges" : [ "black", "blue" ], "points" : [ { "points" : 78, "bonus" : 8 }, { "points" : 57, "bonus" : 7 } ] }
6. 總結
到這裡,我們已經差不多講完了mongo中有關查詢語句的語法。從對簡單值的查詢,到對嵌套子文檔的查詢,再到數組的查詢,最後到數組和子文檔嵌套的複雜查詢。以及如何對多個查詢條件做“or”操作和“and”操作。希望這篇文章對各位有一些幫助吧。
講了那麼多,光看沒用,還是要多動手實踐,自己動手去敲一遍代碼才會加深印象,如果能在項目中的業務場景中需要用到這些查詢,那麼就再好不過了。因為這樣才會讓這些知識更好地被吸收,不然就會掉入學了忘,忘了學的深淵之中。這其實也是寫博客的好處,學了新的知識,即使不能馬上用到工作中,通過一篇博客來鞏固加深印象,雖然沒在實際項目中使用產生的效果好,也是也是有一定的效果的。