MongoDB特定類型的查詢語句實例
MongoDB 在一個文檔中可以使用多種類型的數(shù)據(jù),其中一些類型在查詢時會有特別的行為。
null
null 的行為有一些特別。它可以與自身匹配,所以如果有一個包含如下文檔的集合:
> var documents = [ ... {_id: 1, y: null}, ... {_id: 2, y: 1}, ... {_id: 3, y: 2} ... ] > db.myCollection.insertMany(documents) { "acknowledged" : true, "insertedIds" : [ 1, 2, 3 ] }
那么可以按照預期的方式查詢 “y” 鍵為 null 的文檔:
> db.myCollection.find({y: null})
不過,null 同樣會匹配“不存在”這個條件。因此,對一個鍵進行 null 值的請求還會返回缺少這個鍵的所有文檔:
> db.myCollection.find({x: null})
如果僅想匹配鍵值為 null 的文檔,則需要檢查該鍵的值是否為 null,并且通過 "$exists"條件確認該鍵已存在。
> db.myCollection.find({x: {$eq: null, $exists: true}})
正則表達式
“$regex” 可以在查詢中為字符串的模式匹配提供正則表達式功能。正則表達式對于靈活的字符串匹配非常有用。如果要查找所有用戶名為 Joe 或 joe 的用戶,那么可以使用正則表達式進行不區(qū)分大小寫的匹配:
> db.users.find({name: {$regex: /joe/i}})
可以在正則表達式中使用標志(如 i),但這沒有強制要求。如果除了匹配各種大小寫組合形式的“joe”之外,還希望匹配如“joey”這樣的鍵,那么可以改進一下剛剛的正則表達式:
> db.users.find({name: {$regex: /joey?/i}})
MongoDB 會使用 Perl 兼容的正則表達式(PCRE)庫來對正則表達式進行匹配。任何PCRE 支持的正則表達式語法都能被 MongoDB 接受。在查詢中使用正則表達式之前,最好先在 JavaScript shell 中檢查一下語法,這樣可以確保匹配與預想的一致。
MongoDB 可以利用索引來查詢前綴正則表達式(如 /joey/)。索引不能用于不區(qū)分大小寫的搜索(/joey/i)。當正則表達式以插入符號(^)或左錨點(\A)開頭時,它就是“前綴表達式”。如果正則表達式使用了區(qū)分大小寫的查詢,那么當字段存在索引時,則可以對索引中的值進行匹配。如果它也是一個前綴表達式,那么可以將搜索限制在由該索引的前綴所形成的范圍內的值。
正則表達式也可以匹配自身。雖然很少有人會將正則表達式插入數(shù)據(jù)庫中,但是如果你這么做了,那么它也可以匹配到自身。
查詢數(shù)組
查詢數(shù)組元素的方式與查詢標量值相同。假設有一個數(shù)組是水果列表,如下所示:
> db.food.insertOne({fruit: ["apple", "banana", "peach"]}) { "acknowledged" : true, "insertedId" : ObjectId("6358e513cc245bb639adb4d9") }
則下面的查詢可以成功匹配到該文檔:
> db.food.find({fruit: "banana"}).pretty()
這個查詢就好像對一個這樣的(不合法)文檔進行查詢:{“fruit” : “apple”, “fruit” :“banana”, “fruit” : “peach”}。
“$all”
如果需要通過多個元素來匹配數(shù)組,那么可以使用 “$all”。這允許你匹配一個元素列表。假設我們創(chuàng)建了一個包含 3 個元素的集合:
> db.food.insertMany([ ... {_id: 1, fruit: ["apple", "banana", "peach"]}, ... {_id: 2, fruit: ["apple", "kumquat", "orange"]}, ... {_id: 3, fruit: ["cherry", "banana", "apple"]} ... ]) { "acknowledged" : true, "insertedIds" : [ 1, 2, 3 ] } > db.food.find().pretty() { "_id" : 1, "fruit" : [ "apple", "banana", "peach" ] } { "_id" : 2, "fruit" : [ "apple", "kumquat", "orange" ] } { "_id" : 3, "fruit" : [ "cherry", "banana", "apple" ] }
可以使用 “$all” 查詢來找到同時包含元素 “apple” 和 “banana” 的文檔:
> db.food.find({fruit: {$all: ["apple", "banana"]}})
這里的順序無關緊要。注意,上面第二個結果中的 “banana” 在 “apple” 之前。如果在" a l l " 中使用只有一個元素的數(shù)組,那么這個效果和不使用 " all" 中使用只有一個元素的數(shù)組,那么這個效果和不使用 " all"中使用只有一個元素的數(shù)組,那么這個效果和不使用"all" 是一樣的。例如,{fruit : {$all : [‘apple’]} 和 {fruit : ‘apple’} 會匹配相同的文檔。
也可以使用整個數(shù)組進行精確匹配。不過,精確匹配無法匹配上元素丟失或多余的文檔。
例如,下面這樣可以匹配之前的第一個文檔:
> db.food.find({fruit: ["apple", "banana", "peach"]})
但是下面這樣就不行:
> db.food.find({"fruit" : ["apple", "banana"]})
這樣也無法匹配:
> db.food.find({"fruit" : ["banana", "apple", "peach"]})
如果想在數(shù)組中查詢特定位置的元素,可以使用 key.index 語法來指定下標:
> db.food.find({"fruit.2": "peach"})
數(shù)組下標都是從 0 開始的,因此這個語句會用數(shù)組的第 3 個元素與字符串 “peach” 進行匹配。
“$size”
“$size” 條件運算符對于查詢數(shù)組來說非常有用,可以用它查詢特定長度的數(shù)組,如下所示。
> db.food.find({fruit: {$size: 3}})
一種常見的查詢是指定一個長度范圍。“$size” 并不能與另一個 $ 條件運算符(如"$gt")組合使用,但這種查詢可以通過在文檔中添加一個 “size” 鍵的方式來實現(xiàn)。之后每次向指定數(shù)組添加元素時,同時增加 “size” 的值。如果原本的更新是這樣:
> db.food.update({fruit: {$all: ["apple", "banana"]}}, {$push: {fruit: "strawberry"}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.food.find() { "_id" : 1, "fruit" : [ "apple", "banana", "peach", "strawberry" ] } { "_id" : 2, "fruit" : [ "apple", "kumquat", "orange" ] } { "_id" : 3, "fruit" : [ "cherry", "banana", "apple" ] }
那么可以很容易地轉換成這樣:
> db.food.update({fruit: {$all: ["apple", "banana"]}}, ... {$push: {fruit: "strawberry"}, $inc: {size: 1}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
自增操作的速度非常快,因此任何性能損失都可以忽略不計。這樣存儲文檔后就可以執(zhí)行如下查詢:
> db.food.find({size: {$eq: 1}})
很遺憾,這種技巧無法與 “$addToSet” 運算符聯(lián)合使用。
“$slice”
“$slice” 運算符可以返回一個數(shù)組鍵中元素的子集。
假設現(xiàn)在有一個關于博客文章的文檔,我們希望返回前 10 條評論:
> db.blog.findOne(criteria, {"comments" : {"$slice" : 10}})
同樣,如果想返回后 10 條評論,則可以使用 -10:
> db.blog.findOne(criteria, {"comments" : {"$slice" : -10}})
“$slice” 也可以指定偏移量和返回的元素數(shù)量來獲取數(shù)組中間的結果:
> db.blog.findOne(criteria, {"comments" : {"$slice" : [23, 10]}})
這個操作會略過前 23 個元素,返回第 24~33 個元素。如果數(shù)組中的元素少于 33 個,則會返回盡可能多的元素。
除非特別指定,否則在使用 “$slice” 時會返回文檔中的所有鍵。
可以使用 “$slice” 來獲取最后一條評論,如下所示:
> db.blog.findOne(criteria, {"comments" : {"$slice" : -1}})
返回一個匹配的數(shù)組元素
如果知道數(shù)組元素的下標,那么 “$slice” 非常有用。但有時我們希望返回與查詢條件匹配的任意數(shù)組元素。這時可以使用 $ 運算符來返回匹配的元素。對于前面的博客文章示例,可以這樣獲得 Bob 的評論:
> db.blog.find({"comments.name": "bob"}, {"comments.$": 1})
注意,這種方式只會返回每個文檔中第一個匹配的元素:如果 Bob 在這篇博客中留下了多條評論,那么只有 “comments” 數(shù)組中的第一個會被返回。
數(shù)組與范圍查詢的相互作用
文檔中的標量(非數(shù)組元素)必須與查詢條件中的每一條子句相匹配。如果使用 {“x” :{“ g t " : 10 , " gt" : 10, " gt":10,"lt” : 20}} 進行查詢,那么 “x” 必須同時滿足大于 10 且小于 20。然而,如果文檔中的 “x” 字段是一個數(shù)組,那么當 “x” 鍵的某一個元素與查詢條件的任意一條語句相匹配(查詢條件中的每條語句可以匹配不同的數(shù)組元素)時,此文檔也會被返回。
理解這種行為最好的方式就是來看一個例子。假設現(xiàn)在有如下幾個文檔:
{"x" : 5} {"x" : 15} {"x" : 25} {"x" : [5, 25]}
如果想找出 “x” 的值在 10 和 20 之間的所有文檔,那么你可能會本能地構建這樣的查詢,即 db.test.find({“x” : {“ g t " : 10 , " gt" : 10, " gt":10,"lt” : 20}}),然后期望它會返回一個文檔:{“x” :15}。然而,當實際運行時,我們得到了兩個文檔,如下所示:
> db.test.find({"x" : {"$gt" : 10, "$lt" : 20}}) {"x" : 15} {"x" : [5, 25]}
5 和 25 都不在 10 和 20 之間,但由于 25 與查詢條件中的第一個子句(“x” 的值大于10)相匹配,5 與查詢條件中的第二個子句(“x” 的值小于 20)相匹配,因此這個文檔會被返回。
這樣就使得針對數(shù)組的范圍查詢基本上失去了作用:一個范圍會匹配任何多元素數(shù)組。有幾種方法可以獲得預期的行為。
可以使用 “ e l e m M a t c h " 強制 M o n g o D B 將這兩個子句與單個數(shù)組元素進行比較。不過,這里有一個問題, " elemMatch" 強制 MongoDB 將這兩個子句與單個數(shù)組元素進行比較。不過,這里有一個問題," elemMatch"強制MongoDB將這兩個子句與單個數(shù)組元素進行比較。不過,這里有一個問題,"elemMatch” 不會匹配非數(shù)組元素:
> db.test.find({"x" : {"$elemMatch" : {"$gt" : 10, "$lt" : 20}}}) > // 沒有結果
文檔 {“x” : 15} 不再與查詢條件匹配了,因為它的 “x” 字段不是一個數(shù)組。也就是說,你應該有充分的理由在一個字段中混合數(shù)組和標量值,而這在很多場景中并不需要。對于這樣的情況,“$elemMatch” 為數(shù)組元素的范圍查詢提供了一個很好的解決方案。
如果在要查詢的字段上有索引,那么可以使用 min 和 max 將查詢條件遍歷的索引范圍限制為 “ g t " 和 " gt" 和 " gt"和"lt” 的值:
> db.test.find({"x" : {"$gt" : 10, "$lt" : 20}}).min({"x" : 10}).max({"x" : 20}) {"x" : 15}
現(xiàn)在,這條查詢語句只會遍歷值在 10 和 20 之間的索引,不會與值為 5 和 25 的這兩個條目進行比較。但是,只有在要查詢的字段上存在索引時,才能使用 min 和 max,并且必須將索引的所有字段傳遞給 min 和 max。
在查詢可能包含數(shù)組的文檔的范圍時,使用 min 和 max 通常是一個好主意。在整個索引范圍內對數(shù)組使用 “ g t " / " gt"/" gt"/"lt” 進行查詢是非常低效的。它基本上接受任何值,因此會搜索每個索引項,而不僅僅是索引范圍內的值。
查詢內嵌文檔
查詢內嵌文檔的方法有兩種:查詢整個文檔或針對其單個鍵–值對進行查詢。
查詢整個內嵌文檔的工作方式與普通查詢相同。假設有這樣一個文檔:
{ "name" : { "first" : "Joe", "last" : "Schmoe" }, "age" : 45 }
可以像下面這樣查詢姓名為 Joe Schmoe 的人:
> db.people.find({"name" : {"first" : "Joe", "last" : "Schmoe"}})
然而,如果要查詢一個完整的子文檔,這個子文檔就必須精確匹配。如果 Joe 決定添加一個代表中間名的字段,這個查詢就無法工作了,因為查詢條件不再與整個內嵌文檔相匹配。而且這種查詢還是與順序相關的:{“last” : “Schmoe”, “first” : “Joe”} 就無法匹配。
如果可能,最好只針對內嵌文檔的特定鍵進行查詢。這樣,即使數(shù)據(jù)模式變了,也不會導致所有查詢因為需要精確匹配而無法使用。可以使用點表示法對內嵌文檔的鍵進行查詢:
> db.people.find({"name.first" : "Joe", "name.last" : "Schmoe"})
這時,如果 Joe 增加了更多的鍵,那么這個查詢仍然可以匹配他的姓和名。
這種點表示法是查詢文檔和其他文檔類型的主要區(qū)別。查詢文檔可以包含點,表示“進入內嵌文檔內部”的意思。點表示法也是待插入文檔不能包含 . 字符的原因。當人們試圖將URL 保存為鍵時,常常會遇到這種限制。解決這個問題的一種方法是在插入前或者提取后始終執(zhí)行全局替換,用點字符替換 URL 中不合法的字符。
隨著文檔結構變得越來越復雜,內嵌文檔的匹配可能會變得有點兒棘手。假設我們正在存儲博客文章,要找到 Joe 發(fā)表的 5 分以上的評論??梢园凑找韵路绞綄ξ恼逻M行建模:
> db.blog.find() { "content" : "...", "comments" : [ { "author" : "joe", "score" : 3, "comment" : "nice post" }, { "author" : "mary", "score" : 6, "comment" : "terrible post" } ] }
這時,不能直接使用 db.blog.find({“comments” : {“author” : “joe”, “score” : {“KaTeX parse error: Expected 'EOF', got '}' at position 8: gte" :5}?}}) 進行查詢。內嵌文檔的匹…gte” : 5}})也不行,因為符合作者條件的評論與符合分數(shù)條件的評論可能不是同一條。也就是說,這會返回上面顯示的那個文檔:因為它匹配了第一條評論中的 “author” : “joe” 和第二條評論中的 “score” : 6。
要正確指定一組條件而無須指定每個鍵,請使用 “$elemMatch”。這種模糊的命名條件允許你在查詢條件中部分指定匹配數(shù)組中的單個內嵌文檔。正確的查詢如下所示:
> db.blog.find({"comments" : {"$elemMatch" : {"author" : "joe", "score" : {"$gte" : 5}}}})
“$elemMatch” 允許你將限定條件進行“分組”。僅當需要對一個內嵌文檔的多個鍵進行操作時才會用到它。
總結
到此這篇關于MongoDB特定類型的查詢語句的文章就介紹到這了,更多相關MongoDB特定類型查詢內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
python實現(xiàn)爬蟲數(shù)據(jù)存到 MongoDB
本文給大家分享的是使用python實現(xiàn)將爬蟲爬到的數(shù)據(jù)存儲到mongoDB數(shù)據(jù)庫中的實例代碼,有需要的小伙伴可以參考下2016-09-09Mongodb基本操作與Python連接mongodb并進行基礎操作的方法
mongodb是基于分布式文件存儲的nosql(非關系型)數(shù)據(jù)庫,本文分享了mongodb的基礎操作和Python連接并操作mongodb的基礎方法,基礎的不能再基礎了2018-09-09