MongoDB學習 (六):查詢

本文將介紹操作符的使用,配合操作符,我們可以執行更加複雜的操作。

目錄

  • 查詢操作

  1. 集合查詢方法 find()
  2. 查詢內嵌文檔
  3. 查詢操作符(內含 數組查詢)

 


 

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"} );

  運行結果:


 

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    如果設置了這個修飾符,模式中的沒有經過轉義的或不在字符類中的空白數據字符總會被忽略,並且位於一個未轉義的字符類外部的#字符和下一個換行符之間的字符也被忽略。 這個修飾符使被編譯模式中可以包含註釋。 注意:這僅用於數據字符。 空白字符 還是不能在模式的特殊字符序列中出現,比如序列 。

  注:JavaScript只提供了i和m選項,x和s選項必須使用$regex操作符。

 

"$where"

  操作符功能強大而且靈活,他可以使用任意的JavaScript作爲查詢的一部分,包含JavaScript表達式的字符串或者JavaScript函數。

  新建fruit集合並插入如下文檔:

//插入兩條數據
db.fruit.insert({"apple":1, "banana": 4, "peach" : 4})
db.fruit.insert({"apple":3, "banana": 3, "peach" : 4})

  比較文檔中的兩個鍵的值是否相等.例如查找出banana等於peach鍵值的文檔(4種方法):

複製代碼
//JavaScrip字符串形式
db.fruit.find( { $where: "this.banana == this.peach" } )
db.fruit.find( { $where: "obj.banana == obj.peach" } )
//JavaScript函數形式 db.fruit.find( { $where: function() { return (this.banana == this.peach) } } ) db.fruit.find( { $where: function() { return obj.banana == obj.peach; } } )
複製代碼

 

  查出文檔中存在的兩個鍵的值相同的文檔,JavaScript函數會遍歷集合中的文檔:

複製代碼
>db.fruit.find({$where:function () {
        for (var current in this) {
            for (var other in this) {
                if (current != other && this[current] == this[other]) {
                return true;
                }
            }
        }
        return false;
    }});
複製代碼

  注意:我們儘量避免使用"$Where"査詢,因爲它們在速度上要比常規査詢慢很多。每個文檔都要從BSON轉換成JavaScript對象,然後通過"$where"的表達式來運行;同樣還不能利用索引。

 

"$slice (projection)" 

  $slice操作符控制查詢返回的數組中元素的個數。

語法:db.collection.find( { field: value }, { array: {$slice: count } } );

  此操作符根據參數"{ field: value }" 指定鍵名和鍵值選擇出文檔集合,並且該文檔集合中指定"array"鍵將返回從指定數量的元素。如果count的值大於數組中元素的數量,該查詢返回數組中的所有元素的。

  $slice接受多種格式的參數 包含負數和數組:

//選擇comments的數組鍵值中前五個元素。
db.posts.find( {}, { comments: { $slice: 5 } } );

//選擇comments的數組鍵值中後五個元素。
db.posts.find( {}, { comments: { $slice: -5 } } );

  下面介紹指定一個數組作爲參數。數組參數使用[ skip , limit ] 格式,其中第一個值表示在數組中跳過的項目數,第二個值表示返回的項目數。

//選擇comments的數組鍵值中跳過前20項之後前10項元素
db.posts.find( {}, { comments: { $slice: [ 20, 10 ] } } );

//選擇comments的數組鍵值中倒數第20項起前10項元素
db.posts.find( {}, { comments: { $slice: [ -20, 10 ] } } );

 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章