一、簡介 MongoDB是一款強大、靈活、且易於擴展的通用型資料庫 1、易用性 1)MongoDB是一款面向文檔的資料庫,而不是關係型資料庫,因此而有著更好的擴展性。 2)通過在文檔中嵌入文檔和數組,面向文檔的方法能夠僅使用一條記錄來表現複雜的層級關係。 3)MongoDB沒有預定義模式(prede ...
一、簡介
MongoDB是一款強大、靈活、且易於擴展的通用型資料庫
1、易用性
1)MongoDB是一款面向文檔的資料庫,而不是關係型資料庫,因此而有著更好的擴展性。
2)通過在文檔中嵌入文檔和數組,面向文檔的方法能夠僅使用一條記錄來表現複雜的層級關係。
3)MongoDB沒有預定義模式(predefined schema):文檔的鍵和值不再有固定的類型和大小;這樣沒有固定的模式,添加或刪除欄位就變得容易了。
4)使用MongoDB開發時能夠進行快速迭代,所以開發進程得以加快。
2、易擴展性
PS:當數據量不斷增長,增長到存儲極限時,就需要擴展資料庫了;資料庫的擴展分為縱向擴展和橫向擴展:縱向擴展就是使用性能更好的機器,
橫向擴展就是使用更多的機器。
1)MongoDB的設計採用橫向擴展,它能很容易的在多台伺服器之間進行數據分割
2)MongoDB能夠自動處理跨集群的數據和負載,自動重新分配文檔,並將用戶的請求路由到正確的機器上。
3、豐富的功能
1)索引
- 支持通用二級索引,允許多種快速查詢,且提供唯一索引、複合索引、地理空間索引、全文索引
2)存儲JavaScript
- 開發人員可以直接在服務端存取JavaScript的函數和值
3)聚合
- 支持聚合管道,用戶能通過簡單的片段創建複雜的集合,並通過資料庫自動優化
4)特殊的集合類型
- 支持存在時間有限的集合,適用於那些將在某個時刻過期的數據,如會話session。類似地,MongoDB也支持固定大小的集合,用於保存近期數據,如日誌。
5)文件存儲
- 支持一種非常易用的協議,用於存儲大文件和文件元數據。MongoDB並不具備一些在關係型資料庫中很普遍的功能,如鏈接join和複雜的多行事務。省略
這些的功能是處於架構上的考慮,或者說為了得到更好的擴展性,因為在分散式系統中這兩個功能難以高效地實現。
4、卓越的性能
1)MongoDB把儘可能多的記憶體用作緩存cache,視圖為每次查詢自動選擇正確的索引。
2)只要有可能,資料庫伺服器就會將處理邏輯交給客戶端。這種精簡方式的設計是MongoDB能夠實現如此高性能的原因之一。
二、MongoDB基礎知識
1、文檔是MongoDB的核心概念。文檔就是鍵值對的一個有序集{'msg':'hello','foo':3}。類似於python中的有序字典。
需要註意的是: #1、文檔中的鍵/值對是有序的。 #2、文檔中的值不僅可以是在雙引號裡面的字元串,還可以是其他幾種數據類型(甚至可以是整個嵌入的文檔)。 #3、MongoDB區分類型和大小寫。 #4、MongoDB的文檔不能有重覆的鍵。 #5、文檔中的值可以是多種不同的數據類型,也可以是一個完整的內嵌文檔。文檔的鍵是字元串。除了少數例外情況,鍵可以使用任意UTF-8字元。 文檔鍵命名規範: #1、鍵不能含有\0 (空字元)。這個字元用來表示鍵的結尾。 #2、.和$有特別的意義,只有在特定環境下才能使用。 #3、以下劃線"_"開頭的鍵是保留的(不是嚴格要求的)。
2、集合就是一組文檔。如果將MongoDB中的一個文檔比喻為關係型數據的一行,那麼一個集合就是相當於一張表
1)集合存在於資料庫中,為了管理方便,我們應該將不同格式和類型的數據插到不同的集合,但集合併沒有固定的結構,
也就意味著我們完全可以把不同格式和類型的數據插入到同一個集合中。
2)使用“.”組織子集合
比如一個具有博客功能的應用可能包含兩個集合,分別是blog.posts和blog.authors,這是為了使組織結構更清晰,
這裡的blog集合(這個集合甚至不需要存在)跟它的兩個子集合沒有任何關係;在MongoDB中,使用子集合來組織數據非常高效,值得推薦。
3)當第一個文檔插入時,集合就會被創建。合法的集合名:
- 集合名不能是空字元串""。
- 集合名不能含有\0字元(空字元),這個字元表示集合名的結尾。
- 集合名不能以"system."開頭,這是為系統集合保留的首碼。
- 用戶創建的集合名字不能含有保留字元。
3、資料庫:在MongoDB中,多個文檔組成集合,多個集合可以組成資料庫
資料庫也通過名字來標識。資料庫名可以是滿足以下條件的任意UTF-8字元串: #1、不能是空字元串("")。 #2、不得含有' '(空格)、.、$、/、\和\0 (空字元)。 #3、應全部小寫。 #4、最多64位元組。 有一些資料庫名是保留的,可以直接訪問這些有特殊作用的資料庫。 #1、admin: 從身份認證的角度講,這是“root”資料庫,如果將一個用戶添加到admin資料庫,這個用戶將自動獲得所有資料庫的許可權。
再者,一些特定的伺服器端命令也只能從admin資料庫運行,如列出所有資料庫或關閉伺服器 #2、local: 這個資料庫永遠都不可以複製,且一臺伺服器上的所有本地集合都可以存儲在這個資料庫中 #3、config: MongoDB用於分片設置時,分片信息會存儲在config資料庫中
4、強調:把資料庫名添加到集合名前,得到集合的完全限定名,即命名空間
- 例如:
如果要使用cms資料庫中的blog.posts集合,這個集合的命名空間就是
cmd.blog.posts。命名空間的長度不得超過121個位元組,且在實際使用中應該小於100個位元組
三、基本數據類型
1、在概念上,MongoDB的文檔與Javascript的對象相近,因而可以認為它類似於JSON。JSON(http://www.json.org)是一種簡單的數據表示方式:
其規範僅用一段文字就能描述清楚(其官網證明瞭這點),且僅包含六種數據類型。
2、這樣有很多好處:易於理解、易於解析、易於記憶。然而因為只有null、布爾、數字、字元串、數字和對象這幾種數據類型,
所以JSON的表達能力有一定的局限。
3、雖然JSON具備的這些類型已經具有很強的表現力,但絕大數應用(尤其是在與資料庫打交道時)都還需要其他一些重要的類型。
例如,JSON沒有日期類型,這使得原本容易的日期處理變得麻煩。另外,JSON只有一種數字類型,無法區分浮點數和整數,更別說區分32位和64位了。
再者JSON無法表示其他一些通用類型,如正則表達式或函數。
4、MongoDB在保留了JSON基本鍵/值對特性的基礎上,添加了其他一些數據類型。在不同的編程語言下,這些類型的確切表示有些許差異。
下麵說明瞭MongoDB支持的其他通用類型,以及如何在文檔中使用它們:
#1、null:用於表示空或不存在的欄位 d={'x':null} #2、布爾型:true和false d={'x':true,'y':false} #3、數值 d={'x':3,'y':3.1415926} #4、字元串 d={'x':'egon'} #5、日期 d={'x':new Date()} d.x.getHours() #6、正則表達式 d={'pattern':/^egon.*?nb$/i} 正則寫在//內,後面的i代表: i 忽略大小寫 m 多行匹配模式 x 忽略非轉義的空白字元 s 單行匹配模式 #7、數組 d={'x':[1,'a','v']} #8、內嵌文檔 user={'name':'egon','addr':{'country':'China','city':'YT'}} user.addr.country #9、對象id:是一個12位元組的ID,是文檔的唯一標識,不可變 d={'x':ObjectId()}
5、_id和ObjectId
五、CRUD操作
1、資料庫操作
#1、增 use config #如果資料庫不存在,則創建資料庫,否則切換到指定資料庫。 #2、查 show dbs #查看所有 要想顯示出剛創建的資料庫,我們需要向資料庫插入一些數據。 db.table1.insert({'a':1}) #3、刪 use config #先切換到要刪的庫下 db.dropDatabase() #刪除當前庫
2、集合操作
#1、增 當第一個文檔插入時,集合就會被創建 > use database1 switched to db database1 > db.table1.insert({'a':1}) WriteResult({ "nInserted" : 1 }) > db.table2.insert({'b':2}) WriteResult({ "nInserted" : 1 }) #2、查 > show tables table1 table2 #3、刪 > db.table1.drop() true > show tables table2
3、文檔操作
增
#創建一個user的局部變數,這是一個JavaScript對象 user={ "name":"ming", 'is_nb':true, 'hobbies':['music','read','game'] } db.userinfo.insert(user) db.userinfo.find() user1={ "name":"asb", 'is_sb':true, 'hobbies':['music','sports'] } user2={ "name":"egon", 'is_sb':'wxx', 'hobbies':['music','read','dancing'] } user3={ "name":"alex", 'is_sb':false, 'hobbies':['music','read','girls'] } db.userinfo.insertMany([user1,user2,user3]) #一次插入多個文檔
改

複製代碼 update() 方法用於更新已存在的文檔。語法格式如下: db.collection.update( <query>, <update>, { upsert: <boolean>, multi: <boolean>, writeConcern: <document> } ) 參數說明:對比update db1.t1 set name='EGON',sex='Male' where name='egon' and age=18; query : 相當於where條件。 update : update的對象和一些更新的操作符(如$,$inc...等,相當於set後面的 upsert : 可選,預設為false,代表如果不存在update的記錄不更新也不插入,設置為true代表插入。 multi : 可選,預設為false,代表只更新找到的第一條記錄,設為true,代表更新找到的全部記錄。 writeConcern :可選,拋出異常的級別。 更新操作是不可分割的:若兩個更新同時發送,先到達伺服器的先執行,然後執行另外一個,不會破壞文檔。update用法介紹

#1、最簡單的更新就是用一個新的文檔完全替換匹配的文檔。這適用於大規模式遷移的情況。例如對下麵的用戶文檔做 一個比較大的調整 > db.user.find().pretty() { "_id" : ObjectId("5a5b3a74c126b4b2cbb57e3f"), "name" : "ming", "girl_friends" : 20, "height" : 170 } # 先找到要修改的對象 > var obj=db.user.findOne({'name':'ming'}) > obj { "_id" : ObjectId("5a5b3a74c126b4b2cbb57e3f"), "name" : "ming", "girl_friends" : 20, "height" : 170 } # 修改 > obj.detail={'girl_friends':20,'height':170} { "girl_friends" : 20, "height" : 170} > obj.username=obj.name egon # 刪掉不要的 > delete obj.girl_friends true > delete obj.height true > delete obj.name true # 完成更新 > db.user.update({'name':'ming'},obj) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.user.find() { "_id" : ObjectId("5a5b3a74c126b4b2cbb57e3f"), "detail" : { "girl_friends" : 20, "height" : 170}, "username" : "ming" }簡單更新

2、一個常見的錯誤 db.user.insert({'name':'ming','age':10}) db.user.insert({'name':'ming','age':20}) db.user.insert({'name':'ming','age':30}) > db.user.find() { "_id" : ObjectId("5a5b3ecac126b4b2cbb57e43"), "name" : "ming", "age" : 10 } { "_id" : ObjectId("5a5b3ecbc126b4b2cbb57e44"), "name" : "ming", "age" : 20 } { "_id" : ObjectId("5a5b3ecdc126b4b2cbb57e45"), "name" : "ming", "age" : 30 } # 找到的是第二個ming,他要過生日,於是長了一歲 > var obj=db.user.findOne({'name':'ming','age':20}) > obj.age++ # 我們的條件只定位到第二條記錄這一條記錄,所以更新是沒問題的, > db.user.update({'name':'ming','age':20},obj) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) # 我們的條件定位到了多條記錄,而obj對象的_id是一定的,文檔中的_id是唯一的,我們無法將三條改成同一個_id,所以報錯 > db.user.update({'name':'ming'},obj) #報錯 # 刪掉後,更新成功,但預設改的是第一條 > delete obj._id true > obj { "name" : "ming", "age" : 21 } > db.user.update({'name':'ming'},obj) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.user.find() { "_id" : ObjectId("5a5b40f6c126b4b2cbb57e46"), "name" : "ming", "age" : 21 } { "_id" : ObjectId("5a5b40f6c126b4b2cbb57e47"), "name" : "ming", "age" : 20 } { "_id" : ObjectId("5a5b40f6c126b4b2cbb57e48"), "name" : "ming", "age" : 30 } > # 所以針對這種情況,我們還以_id作為條件保證只定位到我們想要修改的對象,並且比起隨機字元串,_id的查找速度更快 > db.user.update({'_id':ObjectId("5a5b40f6c126b4b2cbb57e47")},obj) # ps: 改多條 > db.user.update({'name':'ming'},obj,{'multi':true}) WriteResult({ "nMatched" : 0, "nUpserted" : 0, "nModified" : 0, "writeError" : { "code" : 9, "errmsg" : "multi update only works with $ operators" #只能用修改器 } }) > db.user.update({'name':'ming'},{$set:obj},{'multi':true}) WriteResult({ "nMatched" : 3, "nUpserted" : 0, "nModified" : 1 })一個常見的錯誤

通常文檔只會有一部分需要更新。可以使用原子性的更新修改器,指定對文檔中的某些欄位進行更新。 更新修改器是種特殊的鍵,用來指定複雜的更新操作,比如修改、增加後者刪除 #=======>1、"$set"修改器 > db.user.find() { "_id" : ObjectId("5a5b51a257147a04b81f2bb4"), "name" : "egon", "age" : 10 } { "_id" : ObjectId("5a5b51a257147a04b81f2bb5"), "name" : "egon", "age" : 20 } { "_id" : ObjectId("5a5b51a357147a04b81f2bb6"), "name" : "egon", "age" : 30 } #完全覆蓋原文檔 > db.user.update({'_id':ObjectId("5a5b51a257147a04b81f2bb4")},{'age':111}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.user.find() { "_id" : ObjectId("5a5b51a257147a04b81f2bb4"), "age" : 111 } { "_id" : ObjectId("5a5b51a257147a04b81f2bb5"), "name" : "egon", "age" : 20 } { "_id" : ObjectId("5a5b51a357147a04b81f2bb6"), "name" : "egon", "age" : 30 } > #$set只更新原文檔的一部分 > db.user.update({'_id':ObjectId("5a5b51a257147a04b81f2bb5")},{'$set':{'age':222}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.user.find() { "_id" : ObjectId("5a5b51a257147a04b81f2bb4"), "age" : 111 } { "_id" : ObjectId("5a5b51a257147a04b81f2bb5"), "name" : "egon", "age" : 222 } { "_id" : ObjectId("5a5b51a357147a04b81f2bb6"), "name" : "egon", "age" : 30 } # 有則更新,無則新增 > db.user.update({'age':30},{'$set':{'addr':{'country':'China','city':'BJ'}}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.user.find() { "_id" : ObjectId("5a5b51a257147a04b81f2bb4"), "age" : 111 } { "_id" : ObjectId("5a5b51a257147a04b81f2bb5"), "name" : "egon", "age" : 222 } { "_id" : ObjectId("5a5b51a357147a04b81f2bb6"), "name" : "egon", "age" : 30, "addr" : { "country" : "China", "city" : "BJ" } } # 修改內嵌文檔 db.user.update({'age':30},{'$set':{'addr.country':'CHINA'}}) # $unset 刪除該鍵值對 db.user.update({'age':30},{'$unset':{'addr':'什麼值都行'}}) #=======>2、增加和減少 "$inc"修改器用來增加已經有的鍵的值(負數則為減少),或者該鍵不存在那就創建一個。只能用於整型、長整型或雙浮點 的值。 對於更新分析數據、因果關係、投票或者其他有變化數值的地方,使用這個都會非法方便 例如: > db.analytics.find() { "_id" : ObjectId("5a5b4cb2126d610970c553ab"), "url" : "http://www.baidu.com", "pv" : 2 } > db.analytics.update({'url':'http://www.baidu.com'},{'$inc':{'pv':1}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.analytics.find() { "_id" : ObjectId("5a5b4cb2126d610970c553ab"), "url" : "http://www.baidu.com", "pv" : 3 } 使用修改器時,_id的值不能改變。其他的鍵值,包括其他的唯一索引的鍵,都是可以更改的。 註意:整個文檔替換時可以改變_id #=======>3、往數組內添加元素 如果數組已經存在,"$push"會向已有的數組末尾加入一個元素,要是沒有就創建一個新的數組。 例如提交博客的評論 db.blog.insert({'_id':1,'name':'alex意外死亡的真相'}) db.blog.update({'_id':1},{'$push':{'comments':{"name":"egon","content":'alex是誰???'}}}) db.blog.update({'_id':1},{'$push':{'comments':{"name":"wxx","content":'我去,真的假的'}}}) db.blog.update({'_id':1},{'$push':{'comments':{"name":"yxx","content":'吃喝嫖賭抽,欠下兩個億'}}}) > db.blog.find().pretty() { "_id" : 1, "name" : "alex意外死亡的真相", "comments" : [ { "name" : "egon", "content" : "alex是誰???" }, { "name" : "wxx", "content" : "我去,真的假的" }, { "name" : "yxx", "content" : "吃喝嫖賭抽,欠下兩個億" } ] }