MongoDB文件操作
一、插入並儲存文件
1.1 insert()方法
db.COLLECTION_NAME.insert(document)
insert()方法是向文件中插入資料最基本的方法,該方法引數接受一個文件,將文件加入到目標集合中。
如將文件{name:"xxx",age:25}插入集合foo中,只需要如下操作:
db.foo.insert({name:"xxx",age:25})
就可在集合中插入該文件。
需要注意的是,因為我們插入的文件中沒有"_id"鍵,所以這個操作會給文件增加一個自動生成的"_id"鍵,然後再將其儲存在資料庫中。這個鍵並不是地址,也不是簡單遞增的,而是通過"時間戳+機器編號+程序編號+序列號"生成,所以每個"_id"都是唯一的。如下圖所示"_id"為:5bbd4dd38f06ac759d5e8a00,就可分解為時間戳(前四位元組):5bbd4dd3 + 機器號(三位元組):8f06ac + 程序編號(兩位元組):759d + 序列號(三位元組):5e8a00 。
1.2 save()方法
save()是一個shell函式,它不僅僅支援插入操作,還支援更新操作,它只有一個引數:文件,說的更詳細些的話,則是該文件的"_id"鍵的值,如果已有一個文件具有相同的"_id"鍵值,則執行更新操作,具體則是呼叫upsert()函式,否則則呼叫insert()執行更新操作。
- db.collection.save(
- <document>,
- {
- writeConcern: <document>
- }
- )
1.3 批量插入
除了insert()方法外,還可使用batchInsert()方法實現批量插入,該函式與insert()方法不同的地方在於,它接受一個文件陣列作為引數。
例子:在集合foo中插入{name:"aaa",age:20}, {name:"bbb",age:25}, {name:"ccc",age:30}三條文件:
- db.foo.batchInsert([{name:"aaa",age:20}, {name:"bbb",age:25}, {name:"ccc",age:30}])
1.4 插入原理與方法
當執行資料插入的時候,驅動程式會把資料轉化成BSON格式,然後將資料匯入資料庫。資料庫會解析BSON,並對其進行最基本的檢查:檢查文件的基本結構。如文件大小(應小於16MB),"_id"欄位等。因此非法資料是無法被甄別的,所以需要對資料來源的資料進行判斷,或者在資料插入資料庫之前做資料校驗。
二、刪除文件
2.1 remove()方法
- db.collection.remove(
- <query>,
- <justOne>
-
)
MongoDB v2.6版本對remove()函式進行了更新,修改了引數。
- db.collection.remove(
- <query>,
- {
- justOne: <boolean>,
- writeConcern: <document>
- }
-
)
該函式的所有引數都為可選引數,如果全為空,則代表刪除集合裡的所有文件。
- query :(可選)刪除的文件的條件。
- justOne : (可選)如果設為 true 或 1,則只刪除一個文件。
- writeConcern :(可選)丟擲異常的級別。
2.2 deleteOne()以及deleteMany()
deleteOne()以及deleteMany()可以認為是remove()方法的拆解,deleteOne()刪除滿足條件的一個文件,deleteMany()刪除滿足條件的所有文件
- db.collection.deleteOne(
- <filter>,
- {
- writeConcern: <document>,
- collation: <document>
- }
- )
- db.collection.deleteMany(
- <filter>,
- {
- writeConcern: <document>,
- collation: <document>
- }
- )
2.3 drop()
drop()方法並不對文件進行操作,而是直接刪除集合,該函式適用於上文remove()無引數的情況,該函式直接刪除集合比刪除所有的文件效率更高。
- db.collection.drop( { writeConcern: <document> } )
三、文件更新
3.1 原子性
在MongoDB中,更新單個的文件操作是原子性的。預設情況下,如果一個update()更新多個文件,那麼對每個文件的更新是原子性的,但是對整個update而言則不是原子性的。有可能存在前一個文件更新成功,後面的文件更新失敗的情況。由於單個文件的更新是原子性的,如果兩個更新同時發生,就會出現阻塞,先到的先執行,所以文件最終結果由靠後的操作決定。
3.2 update()方法
- db.collection.update(
- <query>,
- <update>,
- {
- upsert: <boolean>,
- multi: <boolean>,
- writeConcern: <document>,
- collation: <document>,
- arrayFilters: [ <filterdocument1>, ... ]
- }
- )
update()方法有兩個引數,一個是查詢文件,用於定位需要更新的目標文件;另一個是修改器,用於說明要對找到的文件進行哪些修改。
3.3 文件替換
替換操作是用一個新的文件替換舊的文件,這一般用於修改比較大的情況。該操作需要一箇中間變數來儲存新值,最後再將舊值覆蓋。
下圖使用該操作完成了文件值的更新操作:將"xi"的資訊替換為"xixi",當然,這存在著更好的方法。
另外,使用update()更新時最好指定一個唯一的鍵,如"_id",以防匹配到相同的欄位導致跟新失敗。
3.4 修改器
3.4.1 $set
"$set"可以完成特定字元的修改,如用來指定一個欄位的值。如果該欄位不存在,則建立它。
下圖對"name":"ahn"文件中的"status"值進行修改,將原來的"A"改為"D"。
"$set"還可用來修改鍵的型別以及內嵌文件,使用"$unset"還可將鍵刪除。
3.4.2 $inc
"$inc"可以對資料進行增加或減少,這個操作只針對數字型別,如整形、長整形與雙精度浮點型。如果操作的該鍵不存在,則建立一個。
如將info集合中name: "wangsan"文件對應的age減2:
3.5 陣列修改器
"$push"將資料插入陣列末尾,如果沒有該陣列,則建立一個。
如在info集合中陣列元素的追加,對name為'bob'的文件新增一個數組元素。
"each"與"$push"配合使用,可以一次性push多個值。
-
db.info.update({ "name" : "bob"},{$push: {"detail": {$each: [{"city" : "Beijing"},{ "city" : "Shanghai"}]}}})
上訴操作可在"detail" 中插入兩個值。
"$slice"+"$push"可規定陣列長度,"$slice"的值必須為負整數。
"$sort"則可根據某欄位的值對所有物件進行排序。
3.6 陣列作為資料集
"$addToSet"向陣列中新增元素,如果元素已經存在就不新增。
"$addToSet"+"$set":新增多個不重複的值。
3.7 刪除陣列元素
"$pop"將陣列看成佇列,每次pop都刪除一個元素。{"$pop":{"key":1}}從陣列末尾刪除一個元素,"key":-1則從頭部刪除一個元素。
"$pull"可根據條件來刪除元素。如db.foo.update({},{"$pull":{"class":"English"}})可將class陣列中的"English"元素刪除。
3.8 基於位置的陣列修改器
3.8.1 通過位置
陣列下標可直接作為鍵來選擇元素,且以0開頭。
如過我們要將上圖文件中detail數組裡第一個元素的"class"元素改為"0701",則可用下面的方法:
- db.info.update({ "name" : "bob"},{$set: {"detail.0.class":"0701"}})
3.8.2 定位操作符
但很多時候,我們並不知道需要修改的陣列的下標,所以我們就需要使用定位操作符"$",用來定位查詢文件已經匹配的陣列元素,並進行更新。
如上面的修改"class",使用定位符如下所示:
另外,定位符只更新匹配到的第一個元素。
3.9 修改器速度
最初將文件插入到MongoDB時,一次插入的文件在磁碟上的位置是相鄰的,且文件之間沒有多餘的空間。因此當一個文件變大時,原有的位置就無法放下該文件了,所以這個文件就會被移動位置。
文件建立之初:(該示例源於《MongoDB權威指南》p.43)
當對中間的文件的資料進行增加時,這個文件就會被移動到文件尾部:
當MongoDB不得不移動文件時,它會修改集合的填充因子——為每個新文件預留的增長空間。初始化時,填充因子為1,即完全沒有多餘的空間。在執行完上面的操作後,填充因子擴大為1.5,即每個新加入的文件在之後預留自身1/2大小的空間。如果文件更新操作頻繁,移動次數多,填充因子就會增大。反之,不再有文件移動,填充因子就會緩慢減小。
這也是有時push成為系統瓶頸的原因,文件更新造成文件磁碟結構的變化,導致大量的硬碟讀寫操作。
3.10 多個文件的更新
預設的update操作只會更新第一個匹配,要對資料進行批量更新,則需要使用update的第四個引數,將其設為true,表示是否更新全部查到的文件。而有第四個就需要第三個,第三個引數也接收bool型別,表示是否要將我們更新的文件作文新文件插入,預設為false。
例子:將info集合中將所有status為"B"對應的文件值的status全部改成"B+":
參考文獻:
《MongoDB權威指南》 人們郵電出版社
《NoSQL資料庫原理與應用》 學校編著