一 ?基本概念
MongoDB中數據的結構為:庫、集合、文檔
1 數據庫
多個集合可以組成數據庫。MongoDb的單個實例可以容納多個獨立的數據庫,每個都有自己的集合和權限,不同的數據庫也放置在不同的文件中。
數據庫通過名字來標識,庫名可以是滿足以下條件的任意UTF-8字符串:
不能是空字符串("")。
不得含有' '(空格)、.、$、/、\和\0 (空宇符)。
應全部小寫。
最多64字節。
Mongo保留庫
admin:從權限角度看,這是“root”數據庫。要是將一個用戶添加到這個數據庫,這個用戶自動繼承所有數據庫的權限。一些特定的服務端命令也只能從這個數據庫運行,比如關閉數據庫。
local:這個數據庫不會被復制。可以用來存儲限于需要儲存在本地單臺服務器的集合
config:當Mongo用于分片設置時,config數據庫在內部使用,用于保存分片的信息。
2 集合
集合容納文檔,沒有模式。比起關系型數據庫,集合可以存放任意文檔。但把各種文檔放進一個集合在管理和效率是都不是好的方式,推薦用關系型數據庫的思維來管理文檔結構。
集合通過名字來標識,集合名可以是滿足以下條件的任意UTF-8字符串:
集合名不能是空字符串""。
集合名不能含有\0字符(空字符),這個字符表示集合名的結尾。
集合名不能以"system."開頭,這是為系統集合保留的前綴。
用戶創建的集合名字不能含有保留字符。有些驅動程序的確支持在集合名里面包含,這是因為某些系統生成的集合中包含該字符。除非你要訪問這種系統創建的集合,否則千萬不要在名字里出現$。
3 文檔
文檔是MongoDB中數據的基本單元(類似于關系型數據庫中的行)。多個鍵及其關鍵的值有序的放置在一起便是文檔。MongoDB的文件儲存格式為BSON。
示例:{"name":"dongbo","age":"100"}
注意:
文檔一定是用“{}”括起來
文檔的鍵是雙引號括起來的字符串,不能重復
不能含有\0 (空字符)。這個字符用來表示鍵的結尾。
.和$有特別的意義,只有在特定環境下才能使用。
以下劃線"_"開頭的鍵是保留的(不是嚴格要求的)。
文檔的值可以是字符串、數字、數組、文檔等mongo支持的數據類型。字符串需用雙引號括起來。
MongoDB區分類型和大小寫
鍵值對是有序的。
二 查詢
find函數查詢集合中所有符合查詢條件的文檔并返回結果為游標的文檔集合。findOne查詢一個符合查詢條件的文檔。
在mongo shell中我們不需要JavaScript游標處理方法就可以直接訪問作為查詢結果的文檔集合。當執行查詢操作時,mongo shell直接自動的對游標執行迭代操作并顯示前20條文檔,輸入"it"顯示接下來的20條文檔。
設置顯示文檔數:DBQuery.shellBatchSize=50,設置完后執行find函數就能顯示出前50條文檔記錄。
如果我們通過變量保存find返回的游標,其不會自動進行遍歷:
var cursor = db.inventory.find()
實際發生的是,調用find后,此時并不會真正的訪問數據庫,而是等待開始要求獲取結果的時候才向數據庫發送查詢請求。當調用hasNext或者next方法時才會真正訪問數據庫,這就是懶加載的過程。
先插入一些數據:
db.inventory.insert({"name":"t1","amount":16,"tags":["apple", "banana"]})
db.inventory.insert({"name":"t2","amount":50,"tags":["banana", "orange"]})
db.inventory.insert({"name":"t3","amount":58,"tags":["orange", "apple"]})
1 游標操作
處理游標有三個函數:limit、skip、sort。limit用來限制返回的文檔數量,skip用來指定忽略前面的n條文檔,sort用來對文檔進行排序。三個函數有優先級關系:先sort排序,然后skip跳過前面n個值,最后limit限制文檔最大返回數量。
db.inventory.find({"amount":{$lt:60}}).sort({"amount":1}).skip(1).limit(2)
{ "_id" :ObjectId("55a13b26b26f97b960389a5e"), "name" :"t2", "amount" : 50, "tags" : ["banana", "orange" ] }
{ "_id" :ObjectId("55a13b27b26f97b960389a5f"), "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }
示例中操作語句的寫法只是示意優先級關系,返回結果跟3個函數的書寫順序無關。
示例中按照amount排序,因為amount的值都是整數,所以排序依據數字的大小而來。然而Mongo中對鍵并不會強制要求是什么類型,一個集合中多個文檔的某一個鍵對應的值可能是mongodb支持的不同類型,應對這種情況,MongoDb預先定義了一個順序,從小到大依次為:
(1):最小值
(2):null
(3):數字(整型,長整型,雙精度)
(4):字符串
(5):對象/文檔
(6):數組
(7):二進制數據
(8):對象ID
(9):布爾值
(10):日期型
(11):時間戳
(12):正則表達式
(13):最大值
按照這個順序,便可以跨類型排序。
排序示例:
db.inventory.find().sort({"amount":-1})
{ "_id" :ObjectId("55a13b27b26f97b960389a5f"), "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }
{ "_id" :ObjectId("55a13b26b26f97b960389a5e"), "name" :"t2", "amount" : 50, "tags" : ["banana", "orange" ] }
{ "_id" :ObjectId("55a13b26b26f97b960389a5d"), "name" :"t1", "amount" : 16, "tags" : ["apple", "banana" ] }
限制文檔行數示例:
db.inventory.find().limit(2)
{ "_id" :ObjectId("55a13b26b26f97b960389a5d"), "name" :"t1", "amount" : 16, "tags" : ["apple", "banana" ] }
{ "_id" :ObjectId("55a13b26b26f97b960389a5e"), "name" :"t2", "amount" : 50, "tags" : ["banana", "orange" ] }
2 包裝
包裝查詢會用到一些額外的鍵:
$query
$orderby
$maxscan : integer指定查詢時最多掃描文檔的數量
$min : document查詢的開始條件
$max : document查詢的結束條件
$hint : document指定服務器使用哪些索引進行查詢
$explain : boolean獲取查詢細節,如用到的索引,結果數量,耗時等,類似于關系數據庫這邊查看執行計劃。并不會真正執行查詢
$snapshot:boolean確保查詢的結果是在查詢執行那一刻的一致快照。
示例:
db.inventory.find({$query:{amount:{$lt:58}},$orderby:{amount:-1},$hint:{"name":1}},{"_id":0})
{ "name" :"t1", "amount" : 16, "tags" : ["apple", "banana" ] }
{ "name" :"t2", "amount" : 50, "tags" : ["banana", "orange" ] }
3 查詢指定的鍵
示例:
db.inventory.find({},{"_id":0,"name":1,"tags":1})
{ "name" :"t1", "tags" : [ "apple", "banana" ] }
{ "name" :"t2", "tags" : [ "banana", "orange" ] }
{ "name" :"t3", "tags" : [ "orange", "apple" ] }
db.inventory.find({"amount":{$eq:50}},{"_id":0,"name":1,"tags":1})
{ "name" :"t2", "tags" : [ "banana", "orange" ] }
“0”表示不顯示,“1”表示顯示
4 使用正則表達式
示例:
db.inventory.find({"name":/1/},{"_id":0,"name":1,"tags":1})
{ "name" :"t1", "tags" : [ "apple", "banana" ] }
返回name中匹配“1”的結果
5 去除重復
示例:
db.inventory.distinct("name")
[ "t1","t2", "t3" ]
去重的作用是對某個鍵查詢的結果除去重復結果,關系型數據庫中distinct的作用也一樣
6 查詢集合中總文檔數:
db.inventory.count()
7 查詢內嵌文檔:
查詢文檔有兩種方式,一種是完全匹查詢,另一種是針對鍵/值對查詢。
db.profile.insert({"name" : [{"first" : "db", "last":"z"}]})
db.profile.insert({"name" : [{"first" : "bing", "last" : "pan" }]})
db.profile.insert({"name" : [{"first" : "bol", "last" : "z" }]})
內嵌文檔的完全匹配查詢和數組的完全匹配查詢一樣,內嵌文檔內鍵值對的數量,順序都必須一致才會匹配:
db.profile.find({"name.first":"bol"})
{ "_id" :ObjectId("55a15e53b26f97b960389a6c"), "name" : [ {"first" : "bol", "last" : "z" } ] }
采用針對鍵/值對查詢,通過點來精確表示內嵌文檔的鍵。當內嵌文檔變得復雜后,如鍵的值為內嵌文檔的數組,內嵌文檔的匹配需要些許技巧,例如使用$elemMatch操作符。
db.profile.find({"name":{$elemMatch:{"first":"bing","last":"pan"}}})
{ "_id" :ObjectId("55a15e53b26f97b960389a6b"), "name" : [ {"first" : "bing", "last" : "pan" } ] }
elemMatch投影操作符將限制查詢返回的數組字段的內容只包含匹配elemMatch條件的數組元素。
注意:
數組中元素是內嵌文檔。
如果多個元素匹配$elemMatch條件,操作符返回數組中第一個匹配條件的元素。
8 控制查詢返回的數組中元素的個數
使用$slice操作符,$slice:1的意思是返回數組值中前1個元素
db.inventory.find({amount:{$gt:50}},{"_id":0})
{ "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }
db.inventory.find({amount:{$gt:50}},{tags:{$slice:1},"_id":0})
{ "name" :"t3", "amount" : 58, "tags" : ["orange" ] }
$slice:[1,1],第一個1表示在數組中跳過的項目數,第二個值表示返回的項目數。
db.inventory.find({amount:{$gt:50}},{tags:{$slice:[1,1]},"_id":0})
{ "name" :"t3", "amount" : 58, "tags" : [ "apple"] }
9 查詢操作符:
數組查詢操作符:
$all
用法:{ field : {$all:[value1,value2]}},查詢數組中包含指定值的文檔,條件的數組是文檔中已有數組的子集
示例:
db.inventory.find({"tags":{$all:["apple"]}},{"_id":0})
{ "name" :"t1", "amount" : 16, "tags" : ["apple", "banana" ] }
{ "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }
當只需要匹配數組的一個值時,簡便寫法:
db.inventory.find({"tags":"apple"},{"_id":0})
{ "name" :"t1", "amount" : 16, "tags" : ["apple", "banana" ] }
{ "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }
$size
用法:{ field:{ $size:size } },查詢數組中元素的數量等于size的文檔,size必須是數字
示例:
db.inventory.find({"tags":{$size:2}},{"_id":0})
{ "name" :"t1", "amount" : 16, "tags" : ["apple", "banana" ] }
{ "name" :"t2", "amount" : 50, "tags" : ["banana", "orange" ] }
{ "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }
$elemMatch
用法:{field:{$elemMatch:{exp}}} ,文檔內部存在由文檔作為元素組成數組,如果要以數組中的文檔作為查詢條件,就可以用$elemMatch操作符指向內部文檔。
算術操作符
$gt
用法:{ field : { $gt:value } },查詢鍵值大于指定值的所有文檔
示例:
db.inventory.find({"amount":{$gt:50}},{"_id":0})
{ "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }
$gte
用法:{ field: { $gte: value } },查詢鍵值不小于指定值的所有文檔。
$lt
用法:{ field: { $lt: value } },查詢鍵值小于指定值的所有文檔。
$lte
用法:{ field: { $lte: value } },查詢鍵值不大于指定值的所有文檔。
$eq
用法:{ field: { $eq: value } },查詢鍵值等于指定值的所有文檔。
$ne
用法:{ field: { $ne: value } },查詢鍵值不等于指定值的所有文檔。
$in
用法:{ field: { $in: [value1,value2] } },查詢鍵值等于指定數組中任意值的文檔
示例:
db.inventory.find( { amount : {$in:[50,58]} },{"_id":0})
{ "name" :"t2", "amount" : 50, "tags" : ["banana", "orange" ] }
{ "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }
$nin
用法:{ field: { $nin: [ value1, value2]} },查詢鍵不存在或者鍵值不等于指定數組的任意值的文檔
示例:
db.inventory.find({amount:{$nin:[50,58]}},{"_id":0})
{ "name" :"t1", "amount" : 16, "tags" : ["apple", "banana" ] }
邏輯操作符
$and
用法:{ $and: [ { }, { } ] },$and指定一個至少包含兩個表達式的數組,選擇出滿足該數組中所有表達式的文檔。and操作符使用短路操作,若第一個表達式的值為“false”,余下的表達式將不會執行。
示例:
db.inventory.find({$and:[{"amount":{$gt:50}},{"amount":{$lt:70}}]},{"_id":0})
{ "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }
$or
用法:{ $or: [ { }, { }] },$or執行邏輯OR運算,指定一個至少包含兩個表達式的數組,選擇出至少滿足數組中一條表達式的文檔。
示例:
db.inventory.find({$or:[{"amount":{$gt:50}},{"amount":{$lt:50}}]},{"_id":0})
{ "name" :"t1", "amount" : 16, "tags" : ["apple", "banana" ] }
{ "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }
$nor
用法:{ $nor: [ { }, { }] },$nor執行邏輯NOR運算,指定一個至少包含兩個表達式的數組,選擇出都不滿足該數組中所有表達式的文檔。
示例:
db.inventory.find({$nor:[{"amount":{$gt:50}},{"amount":{$lt:50}}]},{"_id":0})
{ "name" :"t2", "amount" : 50, "tags" : ["banana", "orange" ] }
$not
用法:{ field: { $not: { } } },$not執行邏輯NOT運算,選擇出不能匹配表達式的文檔,包括沒有指定鍵的文檔。$not操作符不能獨立使用,必須跟其他操作一起使用(除$regex)。
示例:
db.inventory.find({"amount":{$not:{$gt:50}}},{"_id":0})
{ "name" :"t1", "amount" : 16, "tags" : ["apple", "banana" ] }
{ "name" :"t2", "amount" : 50, "tags" : ["banana", "orange" ] }
元素操作符
$exists
用法:{ field: { $exists: } },斷言字段是否存在,如果$exists的值為true,選擇存在該字段的文檔;若值為false則選擇不包含該字段的文檔。
示例:
db.inventory.find({qty:{$exists:false}},{"_id":0})
{ "name" :"t1", "amount" : 16, "tags" : ["apple", "banana" ] }
{ "name" :"t2", "amount" : 50, "tags" : ["banana", "orange" ] }
{ "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }
db.inventory.find({amount:{$exists:true,$nin:[16,58]}},{"_id":0})
{ "name" :"t2", "amount" : 50, "tags" : ["banana", "orange" ] }
$mod
用法:{ field: { $mod: [ divisor, remainder ]} },匹配字段值對(divisor)取模,值等于(remainder)的文檔。
示例:
db.inventory.find({amount:{$mod:[5,0]}},{"_id":0})
{ "name" :"t2", "amount" : 50, "tags" : ["banana", "orange" ] }
$type
用法:{ field: { $type: } },選擇字段值為指定的BSON數據類型的文檔.
type>使用下面類型對應的編號:
類型 ? ?類型 ? ?編號
Double ? ?雙精度 ? ?1
String ? ?字符串 ? ?2
Object ? ?對象 ? ?3
Array ? ?數組 ? ?4
Binary data ? ?二進制對象 ? ?5
Object id ? ?對象id ? ?7
Boolean ? ?布爾值 ? ?8
Date ? ?日期 ? ?9
Null ? ?未定義 ? ?10
Regular? Expression ? ?正則表達式 ? ?11
JavaScript ? ?JavaScript代碼 ? ?13
Symbol ? ?符號 ? ?14
JavaScript (with? scope) ? ?JavaScript代碼(帶范圍) ? ?15
32-bit integer ? ?32位整數 ? ?16
Timestamp ? ?時間戳 ? ? 17
64-bit integer ? ? 64位整數 ? ?18
Min key ? ?最小鍵 ? ?255
Max key ? ?最大鍵 ? ?127
如果文檔的鍵值是一個數組。那么$type將對數組里面的元素進行類型匹配而不是鍵值數組本身。
示例:
db.inventory.find({"amount":{$type:1}},{"_id":0})
{ "name" :"t1", "amount" : 16, "tags" : ["apple", "banana" ] }
{ "name" :"t2", "amount" : 50, "tags" : ["banana", "orange" ] }
{ "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }
MinKey和Maxkey表示可能的最小值和最大值,查詢示例:
db.post.insert({x:MinKey})
db.post.find({"x":{$type:-1}})
{ "_id" :ObjectId("55a14d2eb26f97b960389a61"), "x" : {"$minKey" : 1 } }
JavaScript查詢操作符
$regex
regex操作符查詢中可以對字符串的執行正則匹配。MongoDB使用Perl兼容的正則表達式(PCRE)庫來匹配正則表達式,可以使用正則表達式對象或者regex操作符。
options(regex提供四個選項標志)
i如果設置了這個修飾符,忽略大小寫。
m如果設置了這個修飾符,忽略換行。
s如果設置了這個修飾符,模式中的點號元字符匹配所有字符,包含換行符。如果沒有這個修飾符,點號不匹配換行符。
x如果設置了這個修飾符,模式中的沒有經過轉義的或不在字符類中的空白數據字符總會被忽略,并且位于一個未轉義的字符類外部的#字符和下一個換行符之間的字符也被忽略。這個修飾符使被編譯模式中可以包含注釋。注意:這僅用于數據字符。空白字符還是不能在模式的特殊字符序列中出現,比如序列。
注:JavaScript只提供了i和m選項,x和s選項必須使用$regex操作符。
示例:查詢name鍵值以“3”結尾的文檔
db.inventory.find({name:/3/i},{"_id":0});
{ "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }
db.inventory.find( { name: { $regex: '.3',$options: 'i' } },{"_id":0} )
{ "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }
$where
$where操作符功能強大而且靈活,他可以使用任意的JavaScript作為查詢的一部分,包含JavaScript表達式的字符串或者JavaScript函數。不是非常必要時,一定要避免使用"where"査詢,因為它們在速度上要比常規査詢慢很多,每個文檔都要從BSON轉換成JavaScript對象,然后通過"where"的表達式來運行,同時還不能利用索引。所以,只在走投無路時才考慮"$where"這種用法。
示例:比較文檔中的兩個鍵的值是否相等。
db.fruit.insert({"apple":1,"banana": 4, "peach" : 4})
db.fruit.insert({"apple":3,"banana": 3, "peach" : 4})
查找出banana等于peach鍵值的文檔(4種方法):
db.fruit.find( { $where: "this.banana== this.peach" } )
{ "_id" :ObjectId("55a14f35b26f97b960389a62"), "apple" : 1,"banana" : 4, "peach" : 4 }
db.fruit.find( { $where: "obj.banana== obj.peach" } )
{ "_id" :ObjectId("55a14f35b26f97b960389a62"), "apple" : 1,"banana" : 4, "peach" : 4 }
db.fruit.find( { $where: function() {return (this.banana == this.peach) } } )
{ "_id" :ObjectId("55a14f35b26f97b960389a62"), "apple" : 1,"banana" : 4, "peach" : 4 }
db.fruit.find( { $where: function() {return obj.banana == obj.peach; } } )
{ "_id" :ObjectId("55a14f35b26f97b960389a62"), "apple" : 1,"banana" : 4, "peach" : 4 }
查出文檔中存在的兩個鍵的值相同的文檔
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;
?}});
{ "_id" :ObjectId("55a14f35b26f97b960389a62"), "apple" : 1,"banana" : 4, "peach" : 4 }
{ "_id" :ObjectId("55a14f35b26f97b960389a63"), "apple" : 3,"banana" : 3, "peach" : 4 }
三 語法總結
1、find操作的永遠是文檔,所以find()中一定要有{}包裹限定條件
2、限定條件文檔編寫時,如果是key,后面都是跟文檔,如果是操作符,后面跟數組或字符串
3、$and$or$nor$not邏輯或、與、非操作符都是后面跟數組,并在數組中將多個限定文檔作為數組的元素。
4、$gt $lt $gte $lte$eq $ne算術操作符后面都是跟數字
5、$all查詢的是條件數組問文檔數組的子集,$in查詢的是鍵的值在條件數組中