摘要:排序結(jié)果的條件修改器條件,用戶對匹配的文檔進(jìn)行更新和必須指定一個布爾類型,表示是否刪除文檔和必須指定一個布爾類型,表示返回更新前的文檔還是更新后的文檔,默認(rèn)是更新前的文檔。
插入并保存文檔本文所有內(nèi)容以MongoDB 3.2 為基礎(chǔ)。
插入是添加數(shù)據(jù)的基本方法。可以使用insert插入一個文檔:
db.foo.insert({"bar": "baz"})批量插入
使用批量插入,可以加快插入的速度。我們可以使用insertMany來實(shí)現(xiàn)批量插入,它接收一個文檔數(shù)組作為參數(shù)
db.foo.insertMany([{"id": 1}, {"id": 2}, {"id": 3}])
一次發(fā)送數(shù)十、數(shù)百乃至數(shù)千個文檔會明顯提高插入的速度。
本方法不能插入多個文檔到多個集合中。只能插入多個文檔到一個集合中。
但是一次性接受的最大消息長度是有限制的。每次接受的文檔數(shù)組長度為1000個。如果超過,則會分次進(jìn)行插入。
如果批量插入的時候,中間有一個文檔插入失敗,那么前面的文檔插入成功,而后面的文檔則全部插入失敗。
insertMany有第三個參數(shù)ordered意味著是否執(zhí)行有序或者無序的插入,默認(rèn)為true(執(zhí)行有序插入),如果為false,則插入的時候會跳過插入失敗的數(shù)據(jù),繼續(xù)后面數(shù)據(jù)的插入。
插入數(shù)據(jù)的時候,Mongo只會對數(shù)據(jù)進(jìn)行基本的檢查:檢查文檔的格式, 如果沒有 "_id" 字段,就自動增加一個; 檢查大小, 所有的文檔都必須小于16MB。這樣的限制主要是為了防止不良的模式設(shè)計(jì), 并且保證性能的一致。
由于MongoDB只進(jìn)行基本的檢查,所以插入非法數(shù)據(jù)非常容易。因此,應(yīng)該只允許信任的源連接數(shù)據(jù)庫。主流語言的驅(qū)動程序都胡izai數(shù)據(jù)插入到數(shù)據(jù)庫之前做大量的數(shù)據(jù)檢驗(yàn)(比如文檔是否過大,文檔是否包含非UTF-8字符串,是否使用了不可識別的類型)。
刪除的命令是:
db.foo.remove({})
上述命令會刪除foo集合中的所有文檔,但是不會刪除集合本身,也不會刪除集合的元信息。
remove()函數(shù)可以接受一個查詢文檔參數(shù)作為可選參數(shù)。給定這個參數(shù)之后,只有符合條件的才會進(jìn)行刪除。
db.foo.remove({"opt-out": true})
刪除文檔是永久性的,不能撤銷,也不能恢復(fù)。
刪除速度刪除文檔通常會快,但是如果要清空整個集合。使用"drop"直接刪除這個集合會更快,然后再這個空集合上面重建各項(xiàng)索引。需要注意的是"drop"不能指定任何條件,因?yàn)檎麄€集合都被刪除,集合的元數(shù)據(jù)都不見了。
更新文檔更新文檔使用的是update,update有兩個參數(shù),一個是查詢文檔,需要定位你需要更新的目標(biāo)文件,一個是修改器文檔,用于說明要對找到的文檔進(jìn)行那些修改。
更新操作是不可分割的。若是兩個文檔更新同時發(fā)生,先到達(dá)服務(wù)器的先執(zhí)行,接著執(zhí)行另外一個,所以,兩個需要同時進(jìn)行的更新會迅速接連完成。此過程不會破壞文檔。
例如要對下面的文檔進(jìn)行一個大的調(diào)整
{ "_id" : ObjectId("57745b2294ec519556ea6040"), "name" : "joe", "friends" : 32.0, "enemies" : 2.0 }
我們希望將friends和enemies兩個字段移到relationships子文檔中,可以這樣實(shí)現(xiàn)
var joe = db.users.findOne({"name": "joe"}); joe.relationships = {"friends": joe.friends, "enemies": joe.enemies}; joe.username = joe.name; delete joe.friends; delete joe.enemies; delete joe.name; db.users.update({"name": "joe"}, joe);
現(xiàn)在可以用findOne來查看更新后的文檔數(shù)據(jù)。
{ "_id" : ObjectId("57745b2294ec519556ea6040"), "username" : "joe", "relationships" : { "friends" : 32.0, "enemies" : 2.0 } }
這里面有個問題。就是說,如果不知道有多個同樣name=joe的文檔的時候,如果盲目update,會造成因?yàn)槎鄠€文檔在替換的時候,因?yàn)?b>_id重復(fù)了,結(jié)果會導(dǎo)致更新失敗。這個時候,我們可以使用_id來作為限定字段,因?yàn)?b>_id在一個集合當(dāng)中是唯一的。對于上面的例子,這才是正確的更新辦法:
db.users.update({"_id": ObjectId("57745b2294ec519556ea6040")}, joe)
使用_id作為查詢條件比使用隨機(jī)字段速度更快,因集合是通過_id來建立的索引。
使用修改器通常文檔只有一部分需要更新。我們可以使用原子性的更新修改器,指定對文檔中的某些字段進(jìn)行更新。更新修改器是一種特殊的鍵,用來指定復(fù)雜的更新操作。
$set修改器比如用戶資料存儲在下面的文檔里面:
{ "_id" : ObjectId("5778a7e487d2bf26ed1188c4"), "name" : "joe", "age" : 30.0, "sex" : "male", "location" : "Wisconsin" }
比如我們想要添加想要的書籍。我們可以這么執(zhí)行:
db.foo.update({"_id" : ObjectId("5778a7e487d2bf26ed1188c4")}, {"$set": {"favorite book": "War and Peace"}})
然后文檔就有了favorite book鍵。$set在key存在的時候就則進(jìn)行覆蓋,如果不存在,則變成新增Key。
{ "_id" : ObjectId("5778a7e487d2bf26ed1188c4"), "name" : "joe", "age" : 30.0, "sex" : "male", "location" : "Wisconsin", "favorite book" : "War and Peace" }
$set可以改變鍵的數(shù)據(jù)類型。比如我們喜歡很多本書。我們可以這么修改。
db.foo.update({"_id" : ObjectId("5778a7e487d2bf26ed1188c4")}, {"$set": {"favorite book": ["Cat"s Cradle", "Foundation Trilogy", "Ender"s Game"]}})
然后用戶不愛看書,可以使用$unset將這個鍵完全刪除:
db.foo.update({"_id" : ObjectId("5778a7e487d2bf26ed1188c4")}, {"$unset": {"favorite book": 1}})
我們也可以去修改內(nèi)嵌文檔。比如如下文檔:
{ "_id" : ObjectId("577906ca0befef90da41a9c6"), "title" : "A Blog Post", "content" : "...", "author" : { "name" : "joe", "email" : "[email protected]" } }
db.foo.update({"_id" : ObjectId("577906ca0befef90da41a9c6")}, {"$set": {"author.name": "joe schmoe"}})
查看文檔:
{ "_id" : ObjectId("577906ca0befef90da41a9c6"), "title" : "A Blog Post", "content" : "...", "author" : { "name" : "joe schmoe", "email" : "[email protected]" } }增加和減少
$inc修改器可以用來增加已有鍵的值,或者該鍵不存在,那么就創(chuàng)建一個。
比如我們有這么一個文檔。
{ "_id" : ObjectId("57790cfe0befef90da41a9c7"), "game" : "pinball", "user" : "joe" }
比如我們給這個文檔增加50
db.foo.update({"_id" : ObjectId("57790cfe0befef90da41a9c7")}, {"$inc": {"score": 50}})數(shù)組追加元素
{ "_id" : ObjectId("5794a4f679b354ae7c0dccad"), "title" : "a blog post", "content" : "xxx" }
我們現(xiàn)在要對這個文檔增加評論:
db.foo.update({"_id": ObjectId("5794a4f679b354ae7c0dccad")}, {"$push": {"comments": {"name": "joe", "email": "[email protected]", "content": "nice post."}}})
我們再一次查看該文檔,就變成了這樣:
{ "_id" : ObjectId("5794a4f679b354ae7c0dccad"), "title" : "a blog post", "content" : "xxx", "comments" : [ { "name" : "joe", "email" : "[email protected]", "content" : "nice post." } ] }
如果comment鍵不存在,它會創(chuàng)建一個值為數(shù)組的comment鍵,并向數(shù)組中添加一條評論。
如果要同時添加多條評論,我們還可以這么辦:
db.foo.update({"_id": ObjectId("5794a4f679b354ae7c0dccad")}, { "$push": { "comments": { "$each": [ {"name": "joe", "email": "[email protected]", "content": "nice post1."}, {"name": "joe", "email": "[email protected]", "content": "nice post2."} ] } } })
查看一下文檔,就發(fā)現(xiàn)已經(jīng)同時添加了兩條評論:
{ "_id" : ObjectId("5794a4f679b354ae7c0dccad"), "title" : "a blog post", "content" : "xxx", "comments" : [ { "name" : "joe", "email" : "[email protected]", "content" : "nice post." }, { "name" : "joe", "email" : "[email protected]", "content" : "nice post1." }, { "name" : "joe", "email" : "[email protected]", "content" : "nice post2." } ] }
如果想讓我們的comment最大只能存儲4條評論,我們將$slice和$push組合在一起使用,這樣就可以保證數(shù)組不會超過設(shè)定好的最大長度:
db.foo.update({"_id": ObjectId("5794a4f679b354ae7c0dccad")}, { "$push": { "comments": { "$each": [ {"name": "joe", "email": "[email protected]", "content": "nice post1."}, {"name": "joe", "email": "[email protected]", "content": "nice post2."} ], "$slice": -4 } } })
{ "_id" : ObjectId("5794a4f679b354ae7c0dccad"), "title" : "a blog post", "content" : "xxx", "comments" : [ { "name" : "joe", "email" : "[email protected]", "content" : "nice post1." }, { "name" : "joe", "email" : "[email protected]", "content" : "nice post2." }, { "name" : "joe", "email" : "[email protected]", "content" : "nice post1." }, { "name" : "joe", "email" : "[email protected]", "content" : "nice post2." } ] }
我們發(fā)現(xiàn)最開始插入的那條評論已經(jīng)不存在了。只保存了最后插入的4條評論。如果我們限制數(shù)組只包含最后插入的10個元素。$slice就必須是負(fù)整數(shù)。$slice如果是正的,那么只會保存最開始插入的4條評論。
將數(shù)組作為數(shù)據(jù)集使用如果我們將數(shù)組作為數(shù)據(jù)集使用,保證數(shù)組內(nèi)的元素不會重復(fù)。我們可以使用$addToSet來實(shí)現(xiàn)。
比如我們有下面這個文檔:
{ "_id" : ObjectId("5794ad1279b354ae7c0dccae"), "username" : "joe", "emails" : [ "[email protected]", "[email protected]", "[email protected]" ] }
比如我們要給這個文檔添加新的郵件地址,我們可以使用$addToSet來實(shí)現(xiàn)避免插入重復(fù)地址:
db.foo.update({"_id" : ObjectId("5794ad1279b354ae7c0dccae")}, { "$addToSet": { "emails": { "$each":[ "[email protected]", "[email protected]" ] } } })
我們向文檔中插入兩個郵箱,我們查看一下文檔,我們發(fā)現(xiàn)數(shù)量只是增加了一個。
{ "_id" : ObjectId("5794ad1279b354ae7c0dccae"), "username" : "joe", "emails" : [ "[email protected]", "[email protected]", "[email protected]", "[email protected]" ] }刪除元素
比如上個文檔,我們需要刪除emails的一個有郵箱。我們可以使用$pop來刪除。比如{"$pop": {"emails": 1}}就是從末尾刪除一個元素,而{"$pop": {"emails": 2}}則是從頭部進(jìn)行刪除。
而比如我們要根據(jù)條件來刪除數(shù)組中的元素,而不是位置。我們可以使用$pull,比如我們刪除[email protected],我們可以執(zhí)行下面的命令:
db.foo.update({"_id" : ObjectId("5794ad1279b354ae7c0dccae")}, { "$pull": { "emails" : "[email protected]" } })
我們查看一下文檔:
{ "_id" : ObjectId("5794ad1279b354ae7c0dccae"), "username" : "joe", "emails" : [ "[email protected]", "[email protected]", "[email protected]" ] }
比如我們想把emails里面的第一個元素修改一下。我們這樣:
db.foo.update({"_id" : ObjectId("5794ad1279b354ae7c0dccae")}, { "$set": { "emails.0" : "[email protected]" } })
這樣就修改成功了:
{ "_id" : ObjectId("5794ad1279b354ae7c0dccae"), "username" : "joe", "emails" : [ "[email protected]", // 這一行已經(jīng)正確修改 "[email protected]", "[email protected]" ] }
比如當(dāng)我們不知道我們要修改的值得位置,我們可以使用$來自動匹配。
比如我們要修改emails的[email protected]為[email protected],我們可以這樣辦:
db.foo.update({"_id" : ObjectId("5794ad1279b354ae7c0dccae"), "emails": "[email protected]"}, { "$set": { "emails.$" : "[email protected]" } })
然后文檔就修改成功了:
{ "_id" : ObjectId("5794ad1279b354ae7c0dccae"), "username" : "joe", "emails" : [ "[email protected]", "[email protected]", "[email protected]" ] }修改器速度
有的修改器運(yùn)行速度很快,比如$inc,因?yàn)樗恍枰淖兾臋n的大小,只需要將鍵的值修改一下,所以非???。
但是$push會改變文檔的大小,所以就會慢一些($set能在文檔大小不改變的時候立即修改它,否則性能也會有所下降)。
將文檔插入到MongoDB中的時候,依次插入的文檔在磁盤中的位置是相鄰的。如果一個文檔變大了,之前的位置放不下這個文檔了,那么文檔就會被移動到集合的另外一個位置。
如果你的模式在進(jìn)行插入和刪除的時會進(jìn)行大量的移動或者經(jīng)常打亂數(shù)據(jù),可以使用usePowerOf2Sizes來提高磁盤的復(fù)用率。
db.runCommand({"collMod": "集合名稱", "usePowerOf2Sizes": true})
執(zhí)行該命令之后,以后進(jìn)行的所有空間分配,所得到的塊大小都是2的冪。由于這個選項(xiàng)會導(dǎo)致初始空間分配不是那么高效,所以應(yīng)該只在需要經(jīng)常打亂數(shù)據(jù)的集合上面使用。
在一個只進(jìn)行插入或者原地更新的集合上使用這個選項(xiàng),會導(dǎo)致寫入速度變慢。
upsert是一種特殊的更新。要是沒有找到符合更新條件的文檔,就會以這個條件和更新文檔為基礎(chǔ)創(chuàng)建一個新的文檔。如果找到了匹配的文檔,則正常進(jìn)行更新。 upsert非常方便,不必預(yù)置集合,同一套代碼既可以用戶創(chuàng)建文檔,又可以更新文檔。
使用upsert,既可以避免競態(tài)問題,又可以縮減代碼量。具體寫法如下:
db.foo.update({"url": "joe"}, {"$inc": {"pageviews": 1}}, true)
update的第三個參數(shù)表示是否使用upsert,默認(rèn)是false。這行代碼是原子性的,而且特別高效。
有的時候,我們需要創(chuàng)建文檔的時候創(chuàng)建一個字段并為其賦值,但是更新的時候,我們并不需要更新這個字段。我們可以這樣辦。比如created_at這個字段,我們僅僅需要在創(chuàng)建文檔的時候賦值,不需要進(jìn)行更新,我們可以執(zhí)行下列命令:
db.foo.update({"url": "joe"}, {"$setOnInsert": {"created_at": new Date()}}, true)更新多個文檔
默認(rèn)情況下,更新只能對符合匹配條件的第一個文檔執(zhí)行操作。要是有多個文檔符合條件,只會更新第一個文檔,其他文檔不會發(fā)生變化。要更新多個文檔,我們可以把update的第四個參數(shù)設(shè)置為true。
比如下面的這條命令:
db.foo.update({"birthday": "1978-10-13"}, {"$set": {"gift": "Happy Birthday!"}}, false, ture)
返回被更新的文檔注意:update以后的行為可能會發(fā)生變化,比如服務(wù)器默認(rèn)只修改一個文檔變?yōu)槟J(rèn)會更新所有匹配的文檔。所以建議顯式指定update的行為或者注意MongoDB的版本更新變化
以上的命令并不能返回被更新的文檔。但是我們可以通過執(zhí)行findAndModify命令來獲得被更新的文檔。
首先介紹一下findAndModify命令可以使用的字段:
findAndModify
字符串,集合名稱
query
查詢文檔,用于檢索文檔的條件。
sort
排序結(jié)果的條件
update
修改器條件,用戶對匹配的文檔進(jìn)行更新(update和remove必須指定一個)
remove
布爾類型,表示是否刪除文檔(update和remove必須指定一個)
new
布爾類型,表示返回更新前的文檔還是更新后的文檔,默認(rèn)是更新前的文檔。
fields
文檔中需要返回的字段(可選)
upsert
布爾類型,值為true表示這是一個upsert。默認(rèn)是false
注意,update和remove必須有一個,也只能有一個。要是沒有匹配的文檔,這個命令會返回一個錯誤。
比如之前的命令我們就可以這么寫:
db.runCommand({"findAndModify": "foo", "query": {"url": "joe"}, "update": {"$inc": {"pageviews": 1}}, "upsert": true}) //更新文檔 db.runCommand({"findAndModify": "foo", "query": {"url": "joe"}, "remove": true}) //刪除文檔寫入安全機(jī)制
寫入安全是一種客戶端設(shè)置,用于控制寫入的安全級別。默認(rèn)情況下,插入、更新和刪除都會一直等待數(shù)據(jù)庫響應(yīng)。然后才會繼續(xù)執(zhí)行。如果遇到錯誤,客戶端會拋出一個錯誤。
兩種最基本的寫入安全機(jī)制是應(yīng)答式寫入和非應(yīng)答式寫入。應(yīng)答式是默認(rèn)的方式:數(shù)據(jù)庫會給出響應(yīng),告訴你寫入操作是否執(zhí)行成功。非應(yīng)答式寫入不返回任何響應(yīng),所以無法知道寫入是否成功。
shell與客戶端程序?qū)Ψ菓?yīng)答式寫入的實(shí)際支持不一樣:shell在執(zhí)行非應(yīng)答式寫入后,會檢查最后一個操作是否成功,然后才會向用戶輸出提示信息。因此,如果在集合上執(zhí)行了一系列無效操作,最后又執(zhí)行了一個有效操作,shell并不會提示錯誤。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/18867.html
摘要:是一個非關(guān)系型數(shù)據(jù)庫存儲形式為鍵值對水平擴(kuò)展很容易常作為緩存數(shù)據(jù)庫來使用的存儲文檔稱之為類似對象字段值可以包含其他的文檔數(shù)組以及文檔數(shù)組和的概念解析概念的概念解釋說明數(shù)據(jù)庫表集合行文檔列域表關(guān)聯(lián)主鍵手動添加自動創(chuàng)建一進(jìn)入數(shù)據(jù)庫的目錄輸入啟動 Mongodb 是一個非關(guān)系型數(shù)據(jù)庫 存儲形式為鍵值對 水平擴(kuò)展很容易 常作為緩存數(shù)據(jù)庫來使用 Mongodb的存儲文檔稱之為 BSON...
摘要:如果沒有找到找到符合條件的文檔,就會以這個條件和更新文檔為基礎(chǔ)新建一個新的文檔。使用它可以快速方便的對文檔進(jìn)行更新。更新多個文檔默認(rèn)情況下,文檔的更新只針對第一個匹配到的文檔,多個條件符合時,其它文檔不會改變。 what is MongoDB ? 面向文檔的數(shù)據(jù)庫 不再有行的概念,不再有預(yù)定義模式 易于拓展 豐富的功能 索引 聚合 特殊的集合類型 文件存儲 高性能 可以一個示...
摘要:如果沒有找到找到符合條件的文檔,就會以這個條件和更新文檔為基礎(chǔ)新建一個新的文檔。使用它可以快速方便的對文檔進(jìn)行更新。更新多個文檔默認(rèn)情況下,文檔的更新只針對第一個匹配到的文檔,多個條件符合時,其它文檔不會改變。 what is MongoDB ? 面向文檔的數(shù)據(jù)庫 不再有行的概念,不再有預(yù)定義模式 易于拓展 豐富的功能 索引 聚合 特殊的集合類型 文件存儲 高性能 可以一個示...
摘要:固定集合可以聲明的容量大小,其行為類似于循環(huán)隊(duì)列。一般來說,固定集合適用于任何想要自動淘汰過期屬性的場景。固定集合的優(yōu)點(diǎn)寫入速度提升。固定集合非常實(shí)用與記錄日志等場景。不可以對固定集合執(zhí)行刪除文檔操作,但可以刪除整個集合。 一 . 什么是固定集合 MongoDB中有一種特殊類型的集合,值得我們特別留意,那就是固定集合(capped collection)。 固定集合可以聲明collec...
閱讀 2457·2021-10-13 09:40
閱讀 3345·2019-08-30 13:46
閱讀 1129·2019-08-29 14:05
閱讀 2964·2019-08-29 12:48
閱讀 3663·2019-08-26 13:28
閱讀 2156·2019-08-26 11:34
閱讀 2291·2019-08-23 18:11
閱讀 1167·2019-08-23 12:26