1.數組的定位修改器
若數組有多個值,只想對其中一部分進行修改.可以通過位置或定位操作符.
如將上篇的email數組的第一個值"295240648@163.com"修改為"295240648@136.com"
db.users.update(
{"userName":"refactor"},
{
"$set":
{
"emails.0":"295240648@136.com"
}
}
)
很多情況下,不預查詢就不知道要修改數組的下標.MongoDB提供了定位操作符"$",用來
定位查詢文檔已經匹配的元素,並進行更新.
2.修改器速度
有的修改器運行較快."$inc"不需要改變文檔的大小,只需要將鍵的值修改一下,所以修改速度很快.
數組修改器可能更改文檔的大小,就會慢一些,"$set"能在文檔大小不發生改變時立即修改,否則性能也會有所下降.
MongoDB預留了些空白給文檔,來適應大小的變化(事實上,系統會根據文檔的通常的大小變化情況來相應
的調整留的空白大小),但是要是超過了原來的空間,最后還是要分配一塊新的空間.空間分配除了會減慢速度,
同時會隨着數組的變長,MongoDB需要更長的時間來遍歷整個數組,對每個數組的修改也會慢下來.
"$push"或者其他數組修改器是推薦使用的.但要是"$push"成為效率瓶頸,就需要將內嵌數組獨立出來,放到
一個單獨的集合里面.
3.upsert
upsert是一個特殊的更新,要是沒有文檔符合更新條件,就會以這個條件和更新文檔為基礎創建一個新文檔.
如果找到了匹配的文檔,則正常更新.upsert不比預置集合,同一套代碼可以創建,又可以更新文檔.
以前創建的網站計數器,就可以采用upsert的方式.如果不存在就創建文檔,如果存在就更新.
db.users.insert(
{
"url":"http://www.cnblogs.com/refactor",
"pageViews":12345
}
)
使用upsert
db.users.update(
{"url":"http://www.cnblogs.com/refactor"},
{"$inc":{"pageViews":1}},
true
)
注意update的第三個參數的意思是:啟用upsert.
從集合去除:
db.users.remove({"url":"http://www.cnblogs.com/refactor"})
使用upsert
db.users.update(
{"url":"http://www.cnblogs.com/refactor"},
{"$inc":{"pageViews":1}},
true
)
4.save shell
save是一個shell函數,可以在文檔不存在時插入,存在時更新.它只有一個參數:文檔.要是這個文檔有
"_id"鍵,save會調用upsert,否則會調用插入.程序員可以很方便的使用這個函數在shell中修改文檔
可以看出db.users.save(x)和db.users.update({"_id":x._id},x)效果是一樣的.
5.更新多個文檔
默認情況下,更新只能對符合條件的第一個文檔執行操作.要使有多個符合條件文檔都得到更新,可以設置update的
第四個參數為true.
只會更新第一個匹配項
db.users.update(
{"url":"http://www.cnblogs.com/refactor"},
{"$set":{"pageViews":50000}}
)
更新所有匹配項
db.users.update(
{"url":"http://www.cnblogs.com/refactor"},
{"$set":{"pageViews":50000}},
false,
true
)
注意:以后可能會更改update的行為(服務器可能默認會更新所有匹配的文檔,只有第四個參數為false的時候才會更新
第一個文檔),所以建議每次都顯示表明要不要進行多文檔更新.
想要知道多文檔更新到底更新了多少文檔,可以用getLastError命令,鍵"n"的值就是要的數字.
可以用findAndModify命令來獲得更新的文檔.
6.瞬間完成
插入,刪除和更新這三個操作都是瞬間完成的,因為他們都不需要等待數據庫響應.這不是異步操作,客戶端將文檔發送給
服務器后就不管了,客戶端不會收到服務器端的響應操作,如:服務器操作失敗.
這個特點的優點是速度快,它只受客戶端發送的速度或網絡速度的制約.但這樣也會出現很多問題,客戶端可能會向根本
不存在的服務器發送文檔等等.
7.安全操作
如果要完成一個電子商系統,如果某人訂購了某物,應用程序需要時間確保訂單順利.當執行時出現了錯誤,還要重來.
MongoDB默認選擇了不安全的版本,因為構建在關系型數據庫的應用程序基本都不關心返回的代碼,也不會檢查
返回碼,但又得等待這個返回碼,這會造成性能下降.
安全的版本在執行完了操作立即運行getLastError命令,來檢查是否執行成功.驅動程序會等待數據庫響應,然后
適當的處理錯誤,一般會拋出異常.這樣開發者就能用自己的語言來處理數據庫的錯誤了.要是操作成功,getLastError
會給出額外的信息作為響應(如:對於更新,刪除文檔,會給出受影響的文檔數量)
安全的代價就是性能.即便忽略客戶端處理異常的開銷(這個開銷一般是重量級的),等待數據庫響應本身的時間比只發送
消息的時間多一個數量級,所以要權衡數據庫的重要性和速度需求.
8.捕獲常規錯誤
安全操作也是調試數據庫"奇怪"行為的好方法.即便安全操作最后會在生產環境中移除,但是開發過程中還是應該大量的
使用,這樣可以避免很多常見的數據庫使用錯誤,最常見的就是鍵重復的錯誤.
鍵重復的錯誤經常發生在試圖將一個已被占用"_id"值插入.MongoDB不允許在一個集合里面有多個"_id"值一樣的文檔,
如果做的是安全插入,發生了鍵重復錯誤,安全檢查會發現這個服務器錯誤,並拋出異常.在不安全模式下,數據庫沒有響應,
所以根本不知道插入失敗了.
9.請求和連接
數據庫會為每一個MongoDB數據庫連接創建一個隊列,存放這個連接的請求.當客戶端發送一個請求,會被放到隊列的末尾.
只有隊列中的請求都執行完畢,后續的請求才會執行.
注意,每個連接都有獨立的隊列,要是打開兩個shell,就有兩個數據庫連接.在一個shell中執行插入,之后在另一個shell中進行
查詢不一定能得到插入的文檔,但是在同一個shell中,插入后再執行查詢一定是能查到的.手動復現這個行為不容易,但在繁忙
的服務器,交錯的插入/查找就很有可能了.當開發者用一個線程插入數據,用伊利個線程檢查是否成功插入時,就會經常的遇到
這個問題.有那么一兩秒時間,好像根本沒有插入數據,但隨后數據又冒出來了.
使用C#的驅動程序要特別注意這種行為,因為驅動程序使用了連接池.為了提高效率,驅動程序和服務器簡歷了多個連接
(一個連接池),並將請求分散到這些連接中去.好在驅動程序提供了一些機制來確保一系列的請求都由一個連接處理.