mongoDB高階查詢整理
本文將介紹操作符的使用,配合操作符,我們可以執行更加複雜的操作。
目錄
-
查詢操作
1.1 集合查詢方法 find()
db.collection.find() 查詢集合中文件並返回結果為遊標的文件集合。
語法:db.collection.find(query, projection)
引數 型別 描述 query 文件 可選. 使用查詢操作符指定查詢條件 projection 文件 可選.使用投影操作符指定返回的鍵。查詢時返回文件中所有鍵值, 只需省略該引數即可(預設省略). 返回值: 匹配查詢條件的文件集合的遊標. 如果指定投影引數,查詢出的文件返回指定的鍵 ,"_id"鍵也可以從集合中移除掉。
注意:在mongo shell中我們不需要JavaScript遊標處理方法就可以直接訪問作為查詢結果的文件集合。mongo shell預設返回遊標中的前20條文件。當執行查詢操作時,mongo shell直接自動的對遊標執行迭代操作並顯示前20條文件。輸入"it"顯示接下來的20條文件。
find的第一個引數是查詢條件,其形式也是一個文件,決定了要返回哪些文件,空的査詢文件{}會匹配集合的全部內容。要是不指定査詢文件,預設就是{},如同SQL中"SELECT * FROM TABLENAME"語句。
//將返回集合中所有文件 db.collection.find() //或者 db.collection.find({})
第一個引數若為鍵/值對時,查詢過程中就意味著執行了條件篩選,就如同我們使用Linq查詢資料庫一樣。下面查詢操作將返回user集合中age鍵值為16的文件集合。
//mongo db db.user.find({age:16}) //Linq to sql dbContext.user.select(p=>p.age==16)
上面的查詢預設執行“==”操作(就如同linq中 p.age==16),文件中若存在相同鍵的值和查詢文件中鍵的值相等的話,就會返回該文件。
第一個引數若包含多個鍵/值對(逗號分隔),則相當於查詢AND組合條件,“條件1 AND條件2 AND…AND 條件N".例如查詢年齡為28且性別為男性的文件集合:
//mongo db db.user.find({age:28,sex:"male"}) //Linq to sql dbContext.user.select(p=>p.age==28&&p.sex=="male") //SQL SELECT * FROM user WHERE age=28 AND sex="male"
指定返回的鍵
我們可以通過find 的第二個引數來指定返回的鍵。
若find不指定第二個引數,查詢操作預設返回查詢文件中所有鍵值。像SQL中我們可以指定查詢返回欄位一樣 ,mongo中也可以指定返回的鍵,這樣我們就可以避免查詢無用鍵值查詢所消耗的資源、會節省傳輸的資料量和記憶體消耗。
集合user包含 _id,name,age,sex,email等鍵,如果查詢結果想只顯示集合中的"name"和"age"鍵,可以使用如下查詢返回這些鍵,。
> db.users.find({}, {"name" : 1, "age" : 1})
上面查詢結果中,"_id"這個鍵總是被返回,即便是沒有指定也一樣。但是我們可以顯示的將其從查詢結果中移除掉。
> db.users.find({}, {"name" : 1, "age" : 1, "_id":0})
在第二個引數中,指定鍵名且值為1或者true則是查詢結果中顯示的鍵;若值為0或者false,則為不顯示鍵。文件中的鍵若在引數中沒有指定,查詢結果中將不會顯示(_id例外)。這樣我們就可以靈活顯示宣告來指定返回的鍵。
我們在使用RDMS時,有時會對錶中多個欄位之間進行比較。如表store中,有銷售數量soldnum和庫存數量stocknum兩個欄位,我們要查詢表中銷售數量等於庫存數量的記錄時可以使用下面的sql語句:
SELECT * FROM store WHERE soldnum=stocknum
那麼換成mongodb呢,使用find()能實現類似的功能嗎?
> db.store.find ({ "soldnum" : "stocknum"}) //或者 > db.store.find ({ "stocknum":"soldnum" })
結果是不行的!!我們可以使用$where運算子來進行相應的操作。
1.2 查詢內嵌文件
查詢文件有兩種方式,一種是完全匹查詢,另一種是針對鍵/值對查詢。
> db.profile.find()
{ "_id" : ObjectId("51d7b0d436332e1a5f7299d6"), "name" : { "first" : Barack", "last" : "Obama" } }
>
內嵌文件的完全匹配查詢和陣列的完全匹配查詢一樣,內嵌文件內鍵值對的數量,順序都必須一致才會匹配:
> db.profile.find({ name : { first : "Barack", last : "Obama" } }); { "_id" : ObjectId("51d7b0d436332e1a5f7299d6"), "name" : { "first" : Barack", "last" : "Obama" } } >
//無任何返回值 > db.profile.find({ name : { last : "Obama" , first : "Barack"} }); >
推薦採用針對鍵/值對查詢,通過點表示法來精確表示內嵌文件的鍵:
//查詢結果一樣 db.profile.find({ "name.first" : "Barack" , "name.last" : "Obama"}); //或者 db.profile.find({ "name.last" : "Obama" , "name.first" : "Barack"} );
執行結果:
査詢文件可以包含點,來表達“深入內嵌文件內部”的意思,點表示法也是待插入的文件不能包含的原因。當內嵌文件變得複雜後,如鍵的值為內嵌文件的陣列,內嵌文件的匹配需要些許技巧,例如使用$elemMatch操作符。
集合blogs有如下文件:
{ "content" : ".....", "comment" : [ { "author" : "zhangsan", "score" : 3, "comment" : "shafa!" }, { "author" : "lisi", "score" : 5, "comment" : "lzsb!" } ] }
我們想查詢評論中使用者“zhangsan”是否有評分超過4分的評論內容,但我們利用“點表示法”直接寫是有問題的,這條查詢條件和陣列中不同的文件進行了匹配!
> db.blogs.find({"comment.author":"zhangsan", "comment.score":{"$gte":4}});
上面的結果不是我們期望的,下面使用“$elemMatch”操作符即可將一組條件限定到陣列中單條文件的匹配上:
> db.blogs.find({"comment":{"$elemMatch":{"author":"zhangsan","score":{"$gt":4}}}});
> db.blogs.find({"comment":{"$elemMatch":{"author":"zhangsan","score":{"$gt":2}}}});
1.3 查詢操作符
下面我們將配合查詢操作符來執行復雜的查詢操作,比如元素查詢、 邏輯查詢 、比較查詢操作。
我們使用下面的比較操作符"$gt" 、"$gte"、 "$lt"、 "$lte"(分別對應">"、 ">=" 、"<" 、"<="),組合起來進行範圍的查詢。例如查詢年齡為16-18歲(包含16但不含18)的使用者:
>db.user.find( { age: { $gte: 16 ,$lt:18} }
我們可以使用"$ne"來進行"不相等"操作。例如查詢年齡不為18歲的使用者:
>db.user.find( { age: {$ne:18} }
精確匹配日期要精確到毫秒,然而我們通常只是想得到關於一天、一週或者是一個月的資料,我們可以使用"gt"、"
lt"進行範圍査詢。例如,要査找在1990年1月1日出生的使用者:
> start = new Date("1990/01/01") > db.users.find({"birthday" : {"$lt" : start}})
鍵值為null查詢操作
如何檢索出sex鍵值為null的文件,我們使用"in"、"
where"操作符,"$in"判斷鍵值是否為null,"$exists"判定集合中文件是否包含該鍵。
//集合中有一條sex鍵值為null的文件 {"name":"xiaoming","age":20,"sex":"male"} {"name":"xiaohong","age":22,"sex":"female"} {"name":"lilei","age":24,"sex":null} //返回文件中存在sex鍵,且值為null的文件 db.users.find({sex:{$in:[null],$exists:true }}) //返回文件中存在birthday鍵,且值為null的文件 //文件沒有birthday鍵,所以結果為空 db.users.find({birthday:{$in:[null],$exists:true }})
執行截圖:
我們也可以執行如下語句:
> db.users.find({sex:null})
查詢結果跟語句"db.users.find({sex:{in:[null],
exists:true }})"一樣
但是當為我們執行下面語句時,發現查詢結果跟語句"db.users.find({birthday:{in:[null],
exists:true }})"不一樣!
> db.users.find({birthday:null})
查詢返回了所有的文件!
因為null不僅僅匹配自身,而且匹配鍵“不存在的”文件,集合眾文件都不存在"birthday"鍵,都匹配查詢條件,所以上面的語句會返回所有的文件!
我們最好使用db.users.find({sex:{in:[null],
exists:true }})這種格式。
下面先向集合inventory插入3條資料(下面的演示基於此資料),文件內容如下:
{"name":"t1","amount":16,"tags":[ "school", "book", "bag", "headphone", "appliances" ]}
{"name":"t2","amount":50,"tags":[ "appliances", "school", "book" ]}
{"name":"t3","amount":58,"tags":[ "bag", "school", "book" ]}
"$all"
匹配那些指定鍵的鍵值中包含陣列,而且該陣列包含條件指定陣列的所有元素的文件,陣列中元素順序不影響查詢結果。
語法: { field: { $all: [ <value> , <value1> ... ] }
查詢出在集合inventory中 tags鍵值包含陣列,且該陣列中包含appliances、school、book元素的所有文件:
db.inventory.find( { tags: { $all: [ "appliances", "school", "book" ] } } )
該查詢將匹配tags鍵值包含如下任意陣列的所有文件:
[ "school", "book", "bag", "headphone", "appliances" ]
[ "appliances", "school", "book" ]
查詢結果:
文件中鍵值型別不是陣列,也可以使用$all操作符進行查詢操作,如下例所示"$all"對應的陣列只有一個值,那麼和直接匹配這個值效果是一樣的。
//查詢結果是相同的,匹配amount鍵值等於50的文件 db.inventory.find( { amount: {$all:[50]}} ) db.inventory.find( { amount: 50}} )
要是想查詢陣列指定位置的元素,則需使用key.index語法指定下標,例如下面查詢出tags鍵值陣列中第2個元素為"school"的文件:
> db.inventory.find({"tags.1":"school"})
陣列下標都是從0開始的,所以查詢結果返回陣列中第2個元素為"school"的文件:
"$size"
用其查詢指定長度的陣列。語法:{field: {$size: value} }
查詢集合中tags鍵值包含有3個元素的陣列的所有文件:
> db.inventory.find({tags:{$size:3}})
文件"{"name":"t1","amount":16,"tags":[ "school", "book", "bag", "headphone", "appliances" ]}",tags鍵值陣列包含四個元素,所以不匹配查詢條件。查詢結果:
size必須制定一個定值,不能接受一個範圍值,不能與其他查詢子句組合(比如"
gt")。但有時查詢需求就是需要一個長度範圍,這種情況建立一個計數器欄位,當你增加元素的同時增加計數器欄位值。
//每一次向指定陣列新增元素的時候,"count"鍵值增加1(充當計數功能) db.collection.update({ $push : {field: value}, $inc :{count : 1}}) //比較count鍵值實現範圍查詢 db.collection.find({count : {$gt:2}})
"$in"
匹配鍵值等於指定陣列中任意值的文件。類似sql中in.語法: { field: { $in: [<value1>, <value2>, ... <valueN> ] } }
"$nin"
匹配鍵不存在或者鍵值不等於指定陣列的任意值的文件。類似sql中not in(SQL中欄位不存在使用會有語法錯誤).
語法: { field: { $nin: [ <value1>, <value2> ... <valueN> ]} }
查詢出amount鍵值為16或者50的文件:
db.inventory.find( { amount: { $in: [ 16, 50 ] } } )
//查詢出amount鍵值不為16或者50的文件 db.inventory.find( { amount: { $nin: [ 16, 50 ] } } ) //查詢出qty鍵值不為16或50的文件,由於文件中都不存在鍵qty,所以返回所有文件 db.inventory.find( { qty: { $nin: [ 16, 50 ] } } )
文件中鍵值型別不是陣列,也可以使用$all操作符進行查詢操作,如下例所示"$in"對應的陣列只有一個值,那麼和直接匹配這個值效果是一樣的。
//查詢結果是相同的,匹配amount鍵值等於50的文件 db.inventory.find( { amount: {$in:[50]}} ) db.inventory.find( { amount: 50}} )
"$and"
指定一個至少包含兩個表示式的陣列,選擇出滿足該陣列中所有表示式的文件。$and操作符使用短路操作,若第一個表示式的值為“false”,餘下的表示式將不會執行。
語法: { $and: [ { <expression1> }, { <expression2> } , ... , { <expressionN> } ] }
查詢name鍵值為“t1”,amount鍵值小於50的文件:
db.inventory.find({ $and: [ { name: "t1" }, { amount: { $lt:50 } } ] } )
對於下面使用逗號分隔符的表示式列表,MongoDB會提供一個隱式的$and操作:
//等同於{ $and: [ { name: "t1" }, { amount: { $lt:50 } } ] } db.inventory.find({ name: "t1" , amount: { $lt:50 }} )
"$nor"
執行邏輯NOR運算,指定一個至少包含兩個表示式的陣列,選擇出都不滿足該陣列中所有表示式的文件。
語法: { $nor: [ { <expression1> }, { <expression2> }, ... { <expressionN> } ] }
查詢name鍵值不為“t1”,amount鍵值不小於50的文件:
db.inventory.find( { $nor: [ { name: "t1" }, { qty: { $lt: 50 } } ] } )
若是文件中不存在表示式中指定的鍵,表示式值為false; false nor false 等於 true,所以查詢結果返回集合中所有文件:
db.inventory.find( { $nor: [ { sale: true }, { qty: { $lt: 50 } } ] } )
"$not"
執行邏輯NOT運算,選擇出不能匹配表示式的文件 ,包括沒有指定鍵的文件。$not操作符不能獨立使用,必須跟其他操作一起使用(除$regex)。
語法: { field: { $not: { <operator-expression> } } }
查詢amount鍵值不大於50(即小於等於50)的文件資料
db.inventory.find( { amount: { $not: { $gt: 50 } } } ) //等同於db.inventory.find( { amount: { $lte: 50 } } )
查詢條件中的鍵gty,文件中都不存在無法匹配表示,所以返回集合所有文件資料。
db.inventory.find( { gty: { $not: { $gt: 50 } } } )
"$or"
執行邏輯OR運算,指定一個至少包含兩個表示式的陣列,選擇出至少滿足陣列中一條表示式的文件。
語法: { $or: [ { <expression1> }, { <expression2> }, ... , { <expressionN> } ] }
查詢集合中amount的鍵值大於50或者name的鍵值為“t1”的文件:
db.inventory.find( { $or: [ { amount: { $gt: 50 } }, { name: "t1" } ] } )
"$exists"
如果$exists的值為true,選擇存在該欄位的文件;若值為false則選擇不包含該欄位的文件(我們上面在查詢鍵值為null的文件時使用"$exists"判定集合中文件是否包含該鍵)。
語法: { field: { $exists: <boolean> } }
//查詢不存在qty欄位的文件(所有文件) db.inventory.find( { qty: { $exists: false } }) //查詢amount欄位存在,且值不等於16和58的文件 db.inventory.find( { amount: { $exists: true, $nin: [ 16, 58 ] } } )
如果該欄位的值為null,$exists的值為true會返回該條文件,false則不返回。
//向集合中插入一條amount鍵值為null的文件 {"name":"t4","amount":null,"tags":[ "bag", "school", "book" ]} //0條資料 db.inventory.find( { amount: { $exists: false } } ) //所有的資料 db.inventory.find( { amount: { $exists: true } } )
"$mod"
匹配欄位值對(divisor)取模,值等於(remainder)的文件。
語法: { field: { $mod: [ divisor, remainder ]} }
查詢集合中 amount 鍵值為 4 的 0 次模數的所有文件,例如 amount 值等於 16 的文件
db.inventory.find( { amount: { $mod: [ 4, 0 ] } } )
有些情況下(特殊情況鍵值為null時),我們可以使用mod操作符替代使用求模表達式的
where操作符,因為後者代價昂貴。
db.inventory.find( { $where: "this.amount % 4 == 0" } )
注意:返回結果怎麼不一樣。因為有一條文件的amount鍵值為null,javascript中null進行數值轉換,會返回"0"。所以該條文件匹配where操作符求模式了表達式。當文檔中字段值不存在null,就可以使用
mod替代$where的表示式.
"$regex"
操作符查詢中可以對字串的執行正則匹配。 MongoDB使用Perl相容的正則表示式(PCRE)庫來匹配正則表示式.
我們可以使用正則表示式物件或者$regex操作符來執行正則匹配:
//查詢name鍵值以“4”結尾的文件 db.inventory.find( { name: /.4/i } ); db.inventory.find( { name: { $regex: '.4', $options: 'i' } } );
options(使用
regex )
- i 如果設定了這個修飾符,模式中的字母會進行大小寫不敏感匹配。
- m 預設情況下,PCRE 認為目標字串是由單行字元組成的(然而實際上它可能會包含多行).如果目標字串 中沒有 "\n"字元,或者模式中沒有出現“行首”/“行末”字元,設定這個修飾符不產生任何影響。
- s 如果設定了這個修飾符,模式中的點號元字元匹配所有字元,包含換行符。如果沒有這個修飾符,點號不匹配換行符。
- x 如果設定了這個修飾符,模式中的沒有經過轉義的或不在字元類中的空白資料字元總會被忽略,並且位於一個未轉義的字元類外部的#