學(xué)習(xí)MongoDB--(4-2):MongoDB查詢(數(shù)組财著、內(nèi)嵌文檔和$where)

查詢數(shù)組很容易诵冒,對于數(shù)組耕皮,我們可以這樣理解:數(shù)組中每一個(gè)元素都是這個(gè)鍵值對鍵的一個(gè)有效值境蜕,如下面的例子:我們要查詢出售apple的水果店:

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" ] }>
我們發(fā)現(xiàn)只要包含蘋果的數(shù)組都能被查詢出來。如果要通過多個(gè)元素來匹配數(shù)組凌停,就需要條件操作符"$all"粱年,比如我們要查詢既賣apple又賣banana的水果店:
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”對數(shù)組內(nèi)元素的順序沒有要求罚拟,只要全部包含的數(shù)組都能查詢出來台诗。數(shù)組查詢也可以使用精確匹配的方式,即查詢條件文檔中鍵值對的值也是數(shù)組赐俗,如:
{ "_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的處理方式是完全相同的匹配,即順序與數(shù)量都要一致秃励,上述中第一條文檔和查詢條件的順序不一致氏仗,第三條文檔比查詢條件文檔多一個(gè)元素,都沒有被匹配成功夺鲜!
對于數(shù)組的匹配皆尔,還有一種形式是精確指定數(shù)組中某個(gè)位置的元素匹配,我們前面提到币励,數(shù)組中的索引可以作為鍵使用慷蠕,如我們要匹配水果店售第二種水果是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" ] }>
數(shù)組索引從0開始,我們匹配第二種水果就用furits.1作為鍵食呻。
"$size"條件操作符流炕,可以用來查詢特定長度的數(shù)組的,如我們要查詢賣3種水果的水果店:
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”等仅胞,這是這個(gè)操作符的一個(gè)缺陷每辟。使用這個(gè)操作符我們只能精確查詢某個(gè)長度的數(shù)組。如果實(shí)際中干旧,在查詢某個(gè)數(shù)組時(shí)渠欺,需要按其長度范圍進(jìn)行查詢,這里推薦的做法是:在這個(gè)文檔中額外增加一個(gè)“size”鍵椎眯,專門記錄其中數(shù)組的大小挠将,在對數(shù)組進(jìn)行"$push"操作同時(shí)胳岂,將這個(gè)“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 }>
但這個(gè)方式和修改器"$addToSet"沒法配合使用舔稀,因?yàn)槟銦o法判斷這個(gè)元素是否添加到了數(shù)組中乳丰!
上篇提到了,find函數(shù)的第二個(gè)參數(shù)用于查詢返回哪些鍵内贮,他還可以控制查詢返回?cái)?shù)組的一個(gè)子數(shù)組产园,如下例:我只想查詢水果店售賣說過數(shù)組的前兩個(gè):
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”也可以從后面截取,用復(fù)數(shù)即可贺归,如-1表明截取最后一個(gè)淆两;還可以截取中間部分断箫,如[2,3]拂酣,即跳過前兩個(gè),截取3個(gè)仲义,如果剩余不足3個(gè)婶熬,就全部返回!
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" }>
如果第二個(gè)參數(shù)中有個(gè)鍵使用了條件操作符"$slice"埃撵,則默認(rèn)查詢會(huì)返回所有的鍵赵颅,如果此時(shí)你要忽略哪些鍵,可以手動(dòng)指明暂刘!如:
db.fruitshop.find({}, {"fruits":{"$slice":[3,6]}, "name":0, "_id":0});{ "fruits" : [ "strawberry", "banana" ] }{ "fruits" : [ ] }{ "fruits" : [ "banana" ] }>
【查詢內(nèi)嵌文檔】
查詢文檔有兩種方式饺谬,一種是完全匹查詢,另一種是針對鍵值對查詢谣拣!內(nèi)嵌文檔的完全匹配查詢和數(shù)組的完全匹配查詢一樣募寨,內(nèi)嵌文檔內(nèi)鍵值對的數(shù)量,順序都必須一致才會(huì)匹配森缠,如下例:
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 }>
針對內(nèi)嵌文檔特定鍵值對的查詢是最常用的拔鹰!通過點(diǎn)表示法來精確表示內(nèi)嵌文檔的鍵:
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 }>
我們看,這樣查詢贵涵,所有有效文檔均被查詢到了列肢!通過點(diǎn)表示法,可以表示深入到內(nèi)嵌文檔內(nèi)部的鍵宾茂!利用“點(diǎn)表示法”來查詢內(nèi)嵌文檔瓷马,這也約束了在插入文檔時(shí),任何鍵都不能包含“.” !!
當(dāng)內(nèi)嵌文檔變得復(fù)雜后跨晴,如鍵的值為內(nèi)嵌文檔的數(shù)組欧聘,這種內(nèi)嵌文檔的匹配需要一些技巧,如下例:
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" : "just so so!" }, { "author" : "jimmy", "score" : 5, "comment" : "cool! good!" } ] }>
我們想要查詢評論中有叫“joe”并且其給出的分?jǐn)?shù)超過5分的blog文檔坟奥,但我們利用“點(diǎn)表示法”直接寫是有問題的树瞭,因?yàn)檫@條文檔有兩條評論拇厢,一條的作者名字叫“joe”但分?jǐn)?shù)只有3,一條作者名字叫“jimmy”晒喷,分?jǐn)?shù)卻給了5孝偎!也就是這條查詢條件和數(shù)組中不同的文檔進(jìn)行了匹配!這不是我們想要的凉敲,我們這里是要使用一組條件而不是單個(gè)指明每個(gè)鍵衣盾,使用條件操作符“$elemMatch”即可!他能將一組條件限定到數(shù)組中單條文檔的匹配上:
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" : "just so so!" }, { "author" : "jimmy", "score" : 5, "comment" : "cool! good!" } ] }>
這樣做爷抓,結(jié)果是正確的势决!利用條件操作符“$elemMatch”可以組合一組條件,并且還能達(dá)到的“點(diǎn)表示法”的模糊查詢的效果蓝撇!
【$where】
上面提到的所有的鍵值對的查詢方式果复,我們也可以看出,已經(jīng)很強(qiáng)大了渤昌!但如果實(shí)際中真的遇到一種情況無法用上述方式實(shí)現(xiàn)時(shí)虽抄,不用慌,MongoDB為我們提供了終極武器:"$where"独柑,用他可以執(zhí)行任意JavaScript作為查詢的一部分迈窟!最典型的應(yīng)用:一個(gè)文檔,如果有兩個(gè)鍵的值相等忌栅,就選出來车酣,否則不選:
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"其實(shí)就是寫了一個(gè)javascript函數(shù)索绪,MongoDB在查詢時(shí)湖员,會(huì)將每個(gè)文檔轉(zhuǎn)換成一個(gè)javascript對象,然后扔到這個(gè)函數(shù)中去執(zhí)行者春,通過返回結(jié)果來判斷其是否匹配破衔!在實(shí)際使用中,盡量避免使用”$where" 條件操作符钱烟,因?yàn)槠湫阅芎懿钗福≡趫?zhí)行過程中,需要把每個(gè)檔案轉(zhuǎn)化為javascript對象拴袭!如果不可避免读第,則盡量這樣寫:find({”other“:”......“,......拥刻,“$where”:""})怜瞒,即將"$where"放最后,作為結(jié)果調(diào)優(yōu),讓常規(guī)查詢作為前置過濾條件吴汪!這樣能減少一些性能損失惠窄!
我們這里還可以發(fā)現(xiàn),“$where”條件操作符也是作為外層文檔的鍵使用漾橙,昨天說“$or”條件操作符是被作為外層文檔的鍵使用杆融。其余目前遇到的條件操作符都是被作為內(nèi)層文檔的鍵使用!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末霜运,一起剝皮案震驚了整個(gè)濱河市脾歇,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌淘捡,老刑警劉巖藕各,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異焦除,居然都是意外死亡激况,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門踢京,熙熙樓的掌柜王于貴愁眉苦臉地迎上來誉碴,“玉大人宦棺,你說我怎么就攤上這事瓣距。” “怎么了代咸?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵蹈丸,是天一觀的道長。 經(jīng)常有香客問我呐芥,道長逻杖,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任思瘟,我火速辦了婚禮荸百,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘滨攻。我一直安慰自己够话,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布光绕。 她就那樣靜靜地躺著女嘲,像睡著了一般。 火紅的嫁衣襯著肌膚如雪诞帐。 梳的紋絲不亂的頭發(fā)上欣尼,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機(jī)與錄音停蕉,去河邊找鬼愕鼓。 笑死钙态,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的菇晃。 我是一名探鬼主播驯绎,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼谋旦!你這毒婦竟也來了剩失?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤册着,失蹤者是張志新(化名)和其女友劉穎拴孤,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體甲捏,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡演熟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了司顿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片芒粹。...
    茶點(diǎn)故事閱讀 38,137評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖大溜,靈堂內(nèi)的尸體忽然破棺而出化漆,到底是詐尸還是另有隱情,我是刑警寧澤钦奋,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布座云,位于F島的核電站,受9級(jí)特大地震影響付材,放射性物質(zhì)發(fā)生泄漏朦拖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一厌衔、第九天 我趴在偏房一處隱蔽的房頂上張望璧帝。 院中可真熱鬧,春花似錦富寿、人聲如沸睬隶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽理疙。三九已至,卻和暖如春泞坦,著一層夾襖步出監(jiān)牢的瞬間窖贤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留赃梧,地道東北人滤蝠。 一個(gè)月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像授嘀,于是被迫代替她去往敵國和親物咳。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容