程式扎記: [ MongoDB 教學 ] Manual - Updating

標籤

2012年6月7日 星期四

[ MongoDB 教學 ] Manual - Updating

來源自 這裡 
Preface : 
透過 interactive shell 中的 update() 函數, 你可以對滿足 matching criteria 的所有文件進行更新. 底下是該函數的語法說明 : 
(Related driver docs: JavaPythonRubyPHPPerl
db.collection.update( criteria, objNew, upsert, multi ) 
Arguments:
* criteria - query which selects the record to update;
* objNew - updated object or $ operators (e.g., $inc) which manipulate the object
* upsert - if this should be an "upsert" operation; that is, if the record(s) do not exist, insert one. Upsert only inserts a single document.
* multi - indicates if all documents matching criteria should be updated rather than just one. Can be useful with the $ operators below.

- upserts 
upsert 參數為 update() 函數的第三個參數, 如果你傳入 true 則意味 "update the document if present; insert (a single document) if missing", 而 MongoDB 透過第一個參數 criteria 來決定更新的文件是否存在. 

- save() in the mongo shell 
在 mongo shell 中, 如果你要更新的文件只有一個, 你可以使用 save() 函數 : 
> // x is some JSON style object
> db.mycollection.save(x); // updates if exists; inserts if new
>
> // equivalent to:
> db.mycollection.update( { _id: x._id }, x, /*upsert*/ true );

Modifier Operations : 
透過 Modifier operations 可以很有效率與方便的更新一個已存在的 document. 考慮下面範例 : 
> db.name.save({name: "John", n: 1}) # 存入一筆資料
> db.name.find() # 目前 collection "name" 的內容
{ "_id" : ObjectId("4fd00f1562e7a3c2c025444d"), "name" : "John", "n" : 1 }
> var j = db.name.findOne({name: 'John'}) # 取回 name='John' 的 document.
> j.n++; # 對取回的 document j.n 加 1
1
> db.name.save(j) # 將變更後的 document 存回 collection (進行變更)
> db.name.find()
{ "_id" : ObjectId("4fd00f1562e7a3c2c025444d"), "name" : "John", "n" : 2 }

上面範例的步驟 : 查詢 > 資料取回 > 修改資料 > 將修改資料存回. 相當直覺也相當沒有效率! 透過 Modifier Operation 可以有以下好處 : 
a modifier update has the advantages of avoiding the latency involved in querying and returning the object. The modifier update also features operation atomicityand very little network data transfer.

要進行 atomic update, 可以使用 update operators (由 '$' 開頭的 operator). 底下是一個簡單範例 : 
> db.name.find()
{ "_id" : ObjectId("4fd00f1562e7a3c2c025444d"), "name" : "John", "n" : 2 }
> db.name.update({name:'John'}, {$inc: {n:1}}); # 透過 Modifier Operation $inc 將 field="n" 加 1. (針對 document : name='John')
> db.name.find()
{ "_id" : ObjectId("4fd00f1562e7a3c2c025444d"), "name" : "John", "n" : 3 }

但你可能希望能同時更新一個以上的 fields, 這時你可以使用 $set operator : 
> db.name.find()
{ "_id" : ObjectId("4fd00f1562e7a3c2c025444d"), "name" : "John", "n" : 3 }
> db.name.update({name: 'John'}, {$set: {n:4, age:31}}) # 更新 document with name='John' : 更新 n->4 ; 新增 age->31 
> db.name.find()
{ "_id" : ObjectId("4fd00f1562e7a3c2c025444d"), "age" : 31, "n" : 4, "name" : "John" }

底下我們來看看其他的 Modifier operators : 
- $inc 
  1. { $inc : { field : value } }  
Increments field by the number value if field is present in the object, otherwise sets field to the number value : 

- $set 
  1. { $set : { field : value } }  
Sets field to value. All datatypes are supported with $set

- $unset 
  1. { $unset : { field : 1} }  
Deletes a given field. v1.3+ 

- $push 
  1. { $push : { field : value } }  
Appends value to field, if field is an existing array, otherwise sets field to the array [value] if field is not present. If field is present but is not an array, an error raised. 

Multiple arrays may be updated in one operation by comma separating the field : value pairs : 
{ $push : { field : value, field2 : value2 } }

- $pushAll 
  1. { $pushAll : { field : value_array } }  
Appends each value in value_array to field, if field is an existing array, otherwise sets field to the array value_array if field is not present. If field is present but is not an array, an error condition is raised. 

- $addToSet and $each 
  1. { $addToSet : { field : value } }  
Adds value to the array only if its not in the array already, if field is an existing array, otherwise sets field to the array value if field is not present. If field is present but is not an array, an error condition is raised. 

To add a list of several values to the set use the $each qualifier : 
  1. { $addToSet : { a : { $each : [ 3 , 5 , 6 ] } } }  
- $pop 
{ $pop : { field : 1 } }

Removes the last element in an array (ADDED in 1.1

{ $pop : { field : -1 } }

Removes the first element in an array (ADDED in 1.1). 

- $pull 
{ $pull : { field : _value } }

Removes all occurrences of value from field, if field is an array. If field is present but is not an array, an error condition is raised. 

In addition to matching an exact value you can also use expressions ($pull is special in this way) : 
{ $pull : { field : {field2: value} } } // removes array elements with field2 matching value
{ $pull : { field : {$gt: 3} } } // removes array elements greater than 3
{ $pull : { field : {} } } // removes array elements meeting match criteria

- $pullAll 
  1. { $pullAll : { field : value_array } }  
Removes all occurrences of each value in value_array from field, if field is an array. If field is present but is not an array, an error condition is raised. 

- $rename Version 1.7.2+ only. 
  1. { $rename : { old_field_name : new_field_name } }  
Renames the field with name 'old_field_name' to 'new_field_name'. Does not expand arrays to find a match for 'old_field_name'. 

- $bit v1.8+. 
  1. {$bit : { field : {and : 5}}}  
  2. {$bit : {field : {or : 43}}}  
  3. {$bit : {field : {and : 5, or : 2}}}  
Does a bitwise update of field. Can only be used with integers. 

The $ positional operator : v1.4+ 
operator $ (by itself) 意指 "position of the matched array item in the query". 可以透過它找到 array member 並進行操作. 假如有範例如下 : 
> db.message.find()
{ "_id" : ObjectId("4fd045d3950c32e448b011ae"), "title" : "ABC", "comments" : [ { "by" : "joe", "votes" : 3 }, { "by" : "john", "votes" :1}...
> db.message.update({'comments.by': 'joe'}, {$inc: {'comments.$.votes' : 1}}, false, true)
> db.message.find()
{ "_id" : ObjectId("4fd045d3950c32e448b011ae"), "title" : "ABC", "comments" : [ { "by" : "joe", "votes" : 4 }, { "by" : "john", "votes" :1}...

上面的 operator $ 是指在 array 中比對到的第一個 matched item. 注意的是這個 positional operator $ 不適合用在 update() 方法中 upsert 參數為 true 情況下. 因為當 criteria 沒有滿足代表該 document 不存在時, "$" 將會被視為 field name 而不是原先的 positional operator 而造成不可預期結果! 
Note : 
Using "$unsetwith an expression "array.$" will result in the array item becoming null, not being removed. You can issue an update with "{$pull:{x:null}}" to remove all nulls. $pull can now do much of this so this example is now mostly historical : 
> t.insert({x: [1,2,3,4,3,2,3,4]})
> t.find()
{ "_id" : ObjectId("4bde2ad3755d00000000710e"), "x" : [ 1, 2, 3, 4, 3, 2, 3, 4 ] }
> t.update({x:3}, {$unset:{"x.$":1}})
> t.find()
{ "_id" : ObjectId("4bde2ad3755d00000000710e"), "x" : [ 1, 2, null, 4, 3, 2, 3, 4 ] }

Upserts with Modifiers : 
如果你在 update() 中設定 upsert=true, 並使用 modifier operation. 則當Collection 中沒有文件滿足 criteria, 該筆 criteria (進行 modifier operation) 會被 insert 到 collection : 
> db.name.find()
{ "_id" : ObjectId("4fd00f1562e7a3c2c025444d"), "age" : 31, "n" : 4, "name" : "John" }
> db.name.update( {name: 'Joe'}, {$inc: {x:1, y:1}}, true); # 因為 Collection 沒有 document with name='Joe', 故插入 document as criteria : name='Joe'
> db.name.find()
{ "_id" : ObjectId("4fd00f1562e7a3c2c025444d"), "age" : 31, "n" : 4, "name" : "John" }
{ "_id" : ObjectId("4fd04bf51b58b89569d691ed"), "name" : "Joe", "x" : 1, "y" : 1 }

但是在 update() 中使用 modifier 還是有些限制. 因為當你有不只一個 modifier 且 modifier 指向同一個 field 的情況不被允許 : 
> db.name.find()
{ "_id" : ObjectId("4fd00f1562e7a3c2c025444d"), "age" : 31, "n" : 4, "name" : "John" }
{ "_id" : ObjectId("4fd04bf51b58b89569d691ed"), "name" : "Joe", "x" : 1, "y" : 1 }

> db.name.update({name:"John"}, {$inc: {x:1}, $set: {x: 5}}) # 兩個 modifier 都指向 field = "x"
Field name duplication not allowed with modifiers

Blocking : 
在 1.5.2 版後, multi updates 有實作 synchronize 保護的功能, 當你有大筆的資料需要插入到 database 但又希望進行 atomic 操作時可以使用 $atomic flag. 底下是簡單範例 : 
db.students.update({score: {$gt: 60}, $atomic: true}, {$set: {pass: true}}, false, true)

補充說明 : 
* Manual > Updating > Atomic Operations 
* Manual > Updating > findAndModify Command

沒有留言:

張貼留言

網誌存檔