【數組】javascript
查詢數組很容易,對於數組,咱們能夠這樣理解:數組中每個元素都是這個鍵值對鍵的一個有效值,以下面的例子:咱們要查詢出售apple的水果店:java
> db.fruitshop.find(); { "_id" : ObjectId("5022518d09248743250688e0"), "name" : "big fruit", "fruits" : [ "apple", "pear", "orange" ] } { "_id" : ObjectId("502251a309248743250688e1"), "name" : "good fruit", "fruits" : [ "banana", "pear", "orange" ] } { "_id" : ObjectId("502251c109248743250688e2"), "name" : "good fruit", "fruits" : [ "banana", "apple", "tomato" ] } > db.fruitshop.find({"fruits":"apple"}); { "_id" : ObjectId("5022518d09248743250688e0"), "name" : "big fruit", "fruits" : [ "apple", "pear", "orange" ] } { "_id" : ObjectId("502251c109248743250688e2"), "name" : "good fruit", "fruits" : [ "banana", "apple", "tomato" ] } >
咱們發現只要包含蘋果的數組都能被查詢出來。若是要經過多個元素來匹配數組,就須要條件操做符"$all",好比咱們要查詢既賣apple又賣banana的水果店:sql
> db.fruitshop.find(); { "_id" : ObjectId("5022518d09248743250688e0"), "name" : "big fruit", "fruits" : [ "apple", "pear", "orange" ] } { "_id" : ObjectId("502251a309248743250688e1"), "name" : "good fruit", "fruits" : [ "banana", "pear", "orange" ] } { "_id" : ObjectId("502251c109248743250688e2"), "name" : "good fruit", "fruits" : [ "banana", "apple", "tomato" ] } > db.fruitshop.find({"fruits":{"$all":["apple","banana"]}}); { "_id" : ObjectId("502251c109248743250688e2"), "name" : "good fruit", "fruits" : [ "banana", "apple", "tomato" ] } >
咱們看,使用「$all」對數組內元素的順序沒有要求,只要所有包含的數組都能查詢出來。數組查詢也能夠使用精確匹配的方式,即查詢條件文檔中鍵值對的值也是數組,如:mongodb
{ "_id" : ObjectId("5022518d09248743250688e0"), "name" : "big fruit", "fruits" : [ "apple", "pear", "orange" ] } { "_id" : ObjectId("5022535109248743250688e4"), "name" : "fruit king", "fruits" : [ "apple", "orange", "pear" ] } { "_id" : ObjectId("502253c109248743250688e5"), "name" : "good fruit", "fruits" : [ "apple", "orange", "pear", "banana" ] } > db.fruitshop.find({"fruits":["apple","orange","pear"]}); { "_id" : ObjectId("5022535109248743250688e4"), "name" : "fruit king", "fruits" : [ "apple", "orange", "pear" ] } >
若是是精確匹配的方式,MongoDB的處理方式是徹底相同的匹配,即順序與數量都要一致,上述中第一條文檔和查詢條件的順序不一致,第三條文檔比查詢條件文檔多一個元素,都沒有被匹配成功!express
對於數組的匹配,還有一種形式是精確指定數組中某個位置的元素匹配,咱們前面提到,數組中的索引能夠做爲鍵使用,如咱們要匹配水果店售第二種水果是orange 的水果店:數組
> db.fruitshop.find(); { "_id" : ObjectId("5022518d09248743250688e0"), "name" : "big fruit", "fruits" : [ "apple", "pear", "orange" ] } { "_id" : ObjectId("5022535109248743250688e4"), "name" : "fruit king", "fruits" : [ "apple", "orange", "pear" ] } { "_id" : ObjectId("502253c109248743250688e5"), "name" : "good fruit", "fruits" : [ "apple", "orange", "pear", "banana" ] } > db.fruitshop.find({"fruits.1":"orange"}); { "_id" : ObjectId("5022535109248743250688e4"), "name" : "fruit king", "fruits" : [ "apple", "orange", "pear" ] } { "_id" : ObjectId("502253c109248743250688e5"), "name" : "good fruit", "fruits" : [ "apple", "orange", "pear", "banana" ] } >
數組索引從0開始,咱們匹配第二種水果就用furits.1做爲鍵。app
"$size"條件操做符,能夠用來查詢特定長度的數組的,如咱們要查詢賣3種水果的水果店:ide
> db.fruitshop.find(); { "_id" : ObjectId("5022518d09248743250688e0"), "name" : "big fruit", "fruits" : [ "apple", "pear", "orange" ] } { "_id" : ObjectId("5022535109248743250688e4"), "name" : "fruit king", "fruits" : [ "apple", "orange", "pear" ] } { "_id" : ObjectId("502253c109248743250688e5"), "name" : "good fruit", "fruits" : [ "apple", "orange", "pear", "banana" ] } > db.fruitshop.find({"fruits":{"$size":3}}); { "_id" : ObjectId("5022518d09248743250688e0"), "name" : "big fruit", "fruits" : [ "apple", "pear", "orange" ] } { "_id" : ObjectId("5022535109248743250688e4"), "name" : "fruit king", "fruits" : [ "apple", "orange", "pear" ] } >
但條件操做符"$size"不能和其餘操做符連用如「$gt」等,這是這個操做符的一個缺陷。使用這個操做符咱們只能精確查詢某個長度的數組。若是實際中,在查詢某個數組時,須要按其長度範圍進行查詢,這裏推薦的作法是:在這個文檔中額外增長一個「size」鍵,專門記錄其中數組的大小,在對數組進行"$push"操做同時,將這個「size」鍵值加1。以下所示:函數
> db.fruitshop.find({"name":"big fruit"}); { "_id" : ObjectId("5022518d09248743250688e0"), "fruits" : [ "apple", "pear", "orange", "strawberry" ], "name" : "big fruit", "size" : 4 } > db.fruitshop.update({"name":"big fruit"}, ... {"$push":{"fruits":"banana"}, "$inc":{"size":1}}, false, false); > db.fruitshop.find({"name":"big fruit"}); { "_id" : ObjectId("5022518d09248743250688e0"), "fruits" : [ "apple", "pear", "orange", "strawberry", "banana" ], "name" : "big fruit", "size" : 5 } >
但這個方式和修改器"$addToSet"無法配合使用,由於你沒法判斷這個元素是否添加到了數組中!性能
find函數的第二個參數用於查詢返回哪些鍵,他還能夠控制查詢返回數組的一個子數組,以下例:我只想查詢水果店售賣說過數組的前兩個:
> db.fruitshop.find(); { "_id" : ObjectId("5022518d09248743250688e0"), "fruits" : [ "apple", "pear", "orange", "strawberry", "banana" ], "name" : "big fruit" } { "_id" : ObjectId("5022535109248743250688e4"), "fruits" : [ "apple", "orange", "pear" ], "name" : "fruit king" } { "_id" : ObjectId("502253c109248743250688e5"), "fruits" : [ "apple", "orange", "pear", "banana" ], "name" : "good fruit" } > db.fruitshop.find({}, {"fruits":{"$slice":2}}); { "_id" : ObjectId("5022518d09248743250688e0"), "fruits" : [ "apple", "pear" ], "name" : "big fruit" } { "_id" : ObjectId("5022535109248743250688e4"), "fruits" : [ "apple", "orange" ], "name" : "fruit king" } { "_id" : ObjectId("502253c109248743250688e5"), "fruits" : [ "apple", "orange" ], "name" : "good fruit" } >
「$slice」也能夠從後面截取,用負數便可,如-1代表截取最後一個;還能夠截取中間部分,如[2,3],即跳過前兩個,截取3個,若是剩餘不足3個,就所有返回!
> db.fruitshop.find(); { "_id" : ObjectId("5022518d09248743250688e0"), "fruits" : [ "apple", "pear", "orange", "strawberry", "banana" ], "name" : "big fruit" } { "_id" : ObjectId("5022535109248743250688e4"), "fruits" : [ "apple", "orange", "pear" ], "name" : "fruit king" } { "_id" : ObjectId("502253c109248743250688e5"), "fruits" : [ "apple", "orange", "pear", "banana" ], "name" : "good fruit" } > db.fruitshop.find({}, {"fruits":{"$slice":-1}}); { "_id" : ObjectId("5022518d09248743250688e0"), "fruits" : [ "banana" ], "name" : "big fruit" } { "_id" : ObjectId("5022535109248743250688e4"), "fruits" : [ "pear" ], "name" : "fruit king" } { "_id" : ObjectId("502253c109248743250688e5"), "fruits" : [ "banana" ], "name" : "good fruit" } > db.fruitshop.find({}, {"fruits":{"$slice":[3,6]}}); { "_id" : ObjectId("5022518d09248743250688e0"), "fruits" : [ "strawberry", "banana" ], "name" : "big fruit" } { "_id" : ObjectId("5022535109248743250688e4"), "fruits" : [ ], "name" : "fruit king" } { "_id" : ObjectId("502253c109248743250688e5"), "fruits" : [ "banana" ], "name" : "good fruit" } >
若是第二個參數中有個鍵使用了條件操做符"$slice",則默認查詢會返回全部的鍵,若是此時你要忽略哪些鍵,能夠手動指明!如:
> db.fruitshop.find({}, {"fruits":{"$slice":[3,6]}, "name":0, "_id":0}); { "fruits" : [ "strawberry", "banana" ] } { "fruits" : [ ] } { "fruits" : [ "banana" ] } >
【內嵌文檔】
查詢文檔有兩種方式,一種是徹底匹查詢,另外一種是針對鍵值對查詢!內嵌文檔的徹底匹配查詢和數組的徹底匹配查詢同樣,內嵌文檔內鍵值對的數量,順序都必須一致纔會匹配,以下例:
> db.staff.find(); { "_id" : ObjectId("50225fc909248743250688e6"), "name" : { "first" : "joe", "middle" : "bush", "last" : "Schmoe" }, "age" : 45 } { "_id" : ObjectId("50225fe209248743250688e7"), "name" : { "first" : "joe", "middle" : "bush" }, "age" : 35 } { "_id" : ObjectId("50225fff09248743250688e8"), "name" : { "middle" : "bush", "first" : "joe" }, "age" : 25 } > db.staff.find({"name":{"first":"joe","middle":"bush"}}); { "_id" : ObjectId("50225fe209248743250688e7"), "name" : { "first" : "joe", "middle" : "bush" }, "age" : 35 } >
針對內嵌文檔特定鍵值對的查詢是最經常使用的!經過點表示法來精確表示內嵌文檔的鍵:
> db.staff.find(); { "_id" : ObjectId("50225fc909248743250688e6"), "name" : { "first" : "joe", "middle" : "bush", "last" : "Schmoe" }, "age" : 45 } { "_id" : ObjectId("50225fe209248743250688e7"), "name" : { "first" : "joe", "middle" : "bush" }, "age" : 35 } { "_id" : ObjectId("50225fff09248743250688e8"), "name" : { "middle" : "bush", "first" : "joe" }, "age" : 25 } > db.staff.find({"name.first":"joe", "name.middle":"bush"}); { "_id" : ObjectId("50225fc909248743250688e6"), "name" : { "first" : "joe", "middle" : "bush", "last" : "Schmoe" }, "age" : 45 } { "_id" : ObjectId("50225fe209248743250688e7"), "name" : { "first" : "joe", "middle" : "bush" }, "age" : 35 } { "_id" : ObjectId("50225fff09248743250688e8"), "name" : { "middle" : "bush", "first" : "joe" }, "age" : 25 } >
咱們看,這樣查詢,全部有效文檔均被查詢到了!經過點表示法,能夠表示深刻到內嵌文檔內部的鍵!利用「點表示法」來查詢內嵌文檔,這也約束了在插入文檔時,任何鍵都不能包含「.」 !!
當內嵌文檔變得複雜後,如鍵的值爲內嵌文檔的數組,這種內嵌文檔的匹配須要一些技巧,以下例:
> db.blogs.findOne(); { "_id" : ObjectId("502262ab09248743250688ea"), "content" : ".....", "comment" : [ { "author" : "joe", "score" : 3, "comment" : "just so so!" }, { "author" : "jimmy", "score" : 5, "comment" : "cool! good!" } ] } > db.blogs.find({"comment.author":"joe", "comment.score":{"$gte":5}}); { "_id" : ObjectId("502262ab09248743250688ea"), "content" : ".....", "comment" : [ { "author" : "joe", "score" : 3, "comment" : "j ust so so!" }, { "author" : "jimmy", "score" : 5, "comment" : "cool! good!" } ] } >
咱們想要查詢評論中有叫「joe」而且其給出的分數超過5分的blog文檔,但咱們利用「點表示法」直接寫是有問題的,由於這條文檔有兩條評論,一條的做者名字叫「joe」但分數只有3,一條做者名字叫「jimmy」,分數卻給了5!也就是這條查詢條件和數組中不一樣的文檔進行了匹配!這不是咱們想要的,咱們這裏是要使用一組條件而不是單個指明每一個鍵,使用條件操做符「$elemMatch」便可!他能將一組條件限定到數組中單條文檔的匹配上:
> db.blogs.findOne(); { "_id" : ObjectId("502262ab09248743250688ea"), "content" : ".....", "comment" : [ { "author" : "joe", "score" : 3, "comment" : "just so so!" }, { "author" : "jimmy", "score" : 5, "comment" : "cool! good!" } ] } > db.blogs.find({"comment":{"$elemMatch":{"author":"joe", "score":{"$gte":5}}}}); > db.blogs.find({"comment":{"$elemMatch":{"author":"joe", "score":{"$gte":3}}}}); { "_id" : ObjectId("502262ab09248743250688ea"), "content" : ".....", "comment" : [ { "author" : "joe", "score" : 3, "comment" : "j ust so so!" }, { "author" : "jimmy", "score" : 5, "comment" : "cool! good!" } ] } >
這樣作,結果是正確的!利用條件操做符「$elemMatch」能夠組合一組條件,而且還能達到的「點表示法」的模糊查詢的效果!
【$where】
上面提到的全部的鍵值對的查詢方式,咱們也能夠看出,已經很強大了!但若是實際中真的遇到一種狀況沒法用上述方式實現時,不用慌,MongoDB爲咱們提供了終極武器:"$where",用他能夠執行任意JavaScript做爲查詢的一部分!最典型的應用:一個文檔,若是有兩個鍵的值相等,就選出來,不然不選:
> db.fruitprice.find(); { "_id" : ObjectId("50226b4c3becfacce6a22a5b"), "apple" : 10, "banana" : 6, "pear" : 3 } { "_id" : ObjectId("50226ba63becfacce6a22a5c"), "apple" : 10, "watermelon" : 3, "pear" : 3 } > db.fruitprice.find({"$where":function () { ... for(var current in this){ ... for(var other in this){ ... if(current != other && this[current] == this[other]){ ... return true; ... } ... } ... } ... return false; ... }}); { "_id" : ObjectId("50226ba63becfacce6a22a5c"), "apple" : 10, "watermelon" : 3, "pear" : 3 } >
咱們能夠看出,使用"$where"其實就是寫了一個javascript函數,MongoDB在查詢時,會將每一個文檔轉換成一個javascript對象,而後扔到這個函數中去執行,經過返回結果來判斷其是否匹配!在實際使用中,儘可能避免使用"$where" 條件操做符,由於其性能不好!在執行過程當中,須要把每一個檔案轉化爲javascript對象!若是不可避免,則儘可能這樣寫:find({"other":"......",......,"$where":""}),即將"$where"放最後,做爲結果調優,讓常規查詢做爲前置過濾條件!這樣能減小一些性能損失!
$where補充:
引用自:https://docs.mongodb.com/manual/reference/operator/query/where/
定義:
「
Use the $where operator to pass either a string containing a JavaScript expression or a full JavaScript function to the query system. The $where provides greater flexibility, but requires that the database processes the JavaScript expression or function for each document in the collection. Reference the document in the JavaScript expression or function using either this or obj .
」
限制:
「
Do not use global variables.
$where evaluates JavaScript and cannot take advantage of indexes. Therefore, query performance improves when you express your query using the standard MongoDB operators (e.g., $gt, $in).
In general, you should use $where only when you can’t express your query using another operator. If you must use $where, try to include at least one other standard query operator to filter the result set. Using $where alone requires a collection scan.
」