MongoDB 教程四: 高级更改操作

(视频来源:php兄弟连)

MongoDB 系列教程索引 : MongoDB 教程索引 (附有视频)

 

db.collection.update()

说明

db.collection.update(query, update, options)
修改一个或多个集合中已经存在的文档记录。这个方法可以修改一个或多个已经存在的文档记录中的指定字段,或替换整个已经存在的文档记录,具体操作由传入的参数( update parameter)决定。

默认情况下, update() 方法只修改 一个 文档记录。设置 “Multi”选项 参数后可以批量更新匹配查询条件的所有文档记录。

The update() method has the following form:

在 2.6 版更改.

db.collection.update(
   <query>,
   <update>,
   {
     upsert: <boolean>,
     multi: <boolean>,
     writeConcern: <document>
   }
)

The update() method takes the following parameters:

Parameter Type Description
query document The selection criteria for the update. Use the same query selectors as used in the find() method.
update document The modifications to apply. For details see Update方法的参数.
upsert boolean Optional. If set to true, creates a new document when no document matches the query criteria. The default value is false, which does not insert a new document when no match is found.
multi boolean Optional. If set to true, updates multiple documents that meet the query criteria. If set to false, updates one document. The default value is false. For additional information, see “Multi”选项.
writeConcern document

Optional. A document expressing the write concern. Omit to use the default write concern. See 安全的写操作.

2.6 新版功能.

在 2.6 版更改: The update() method returns an object that contains the status of the operation.

返回: 更新结果对象 ( 操作结果 )中包含本次操作的状态。

行为

 

安全的写操作

在 2.6 版更改.

The update() method uses the update command, which uses the default write concern. To specify a different write concern, include the writeConcern option in the options parameter. See 覆盖默认的写确认级别 for an example.

 

Update方法的参数

The update() method either modifies specific fields in existing documents or replaces an existing document entirely.

更新指定的字段

如果 <update> 参数中包含更新操作符( update operator ),也就是使用 $set 操作符时:

  • The <update> document must contain only update operator expressions.
  • The update() method updates only the corresponding fields in the document. For an example, see 更新指定的字段.

更新整个文档记录

如果 <update> 参数中 包含 field:value (普通的键值对)表达式,则:

  • The update() method replaces the matching document with the <update> document. The update() method does not replace the _id value. For an example, see 替换全部字段.
  • update() 方法 不会 批量替换文档( update multiple documents )。

 

更新插(Upsert) 选项

更新插(Upsert) 行为

如果 upsert 选项的值是 true 且没有匹配到查询条件的文档, update() 方法会插入 一条 文档记录。update方法会这样插入一条新文档记录:

  • 用传给 <update> 方法的参数,或者

  • 如果 <update> 方法传入的参数中 包含更新操作符( update operator )字段,会同时使用传入的 <query><update> 参数。update方法会用 <query> 参数的值创建一个基础文档记录,再把 <update> 参数中的值应用到这个文档记录中。

如果“upsert“ 选项的值是 true 并且匹配到符合查询条件的文档记录, update() 方法会执行更新操作。

参见

操作符 $setOnInsert

使用唯一索引

警告

如果想要避免插入多个同样的文档记录,当 query 参数的字段中有唯一索引时只要使用 upsert: true 选项即可。

如果 people 集合中没有 name 字段的值是 Andy 的记录,把这条记录插进去。要考虑多个用户同时使用下面的的 更新 操作并且都带了 upsert: true 这个选项的情况:

db.people.update(
   { name: "Andy" },
   {
      name: "Andy",
      rating: 1,
      score: 1
   },
   { upsert: true }
)

如果所有的 update() 操作都完成了 query 这部分工作,但没有任何一个用户完成数据写入, 并且 name 字段上没有唯一索引,这时有可能每个 update 操作都插入了一个记录。

为了阻止 Mongodb 插入多条同样的文档记录,需要在 name 字段上创建一个唯一索引 ( unique index )。有了唯一索引,如果多个应用使用同样的包含 upsert: true 选项的更新操作, 只有一个 update() 操作可以成功插入一条新的文档记录。

其余的update操作会:

  • 更新最近插入的文档,或者

  • 尝试插入重复的记录时失败。

    如果因为出现重复的索引键而产生异常操作会失败,应用程序可以再次尝试这个操作,它会被转为更新操作并执行成功。

 

“Multi”选项

如果 multi 选项被设置成 trueupdate() 方法会更新所有 <query> 条件匹配到的文档记录。 multi 选项可以和其他读写操作符交叉使用。在未分片的集合中,可以使用独占操作符 ( $isolated )忽略批量更新操作, $isolated 操作会让当前更新操作使用独占模式,在操作完成前忽略其他操作。在独占操作进行时,其他使用者都看不到正在被更新的文档记录,直到操作完成或发生异常。

如果update参数 ( <update> )传入的文档参数中 包含 “键:值” 格式的数据(不包含其他操作符), update() 操作 不会 一次更新多条记录。

示例,参见 批量更新文档记录

分片集合

All update() operations for a sharded collection that specify the multi: false option must include the shard key or the _id field in the query specification. update() operations specifying multi: false in a sharded collection without the shard key or the _id field return an error.

 

示例

 

更新指定的字段

如果要修改文档中的指定字段,可以在 <update> 参数中使用更新操作符( update operators )。如果 <update> 参数中有文档中不存在的字段, update() 操作会在文档中加入这些字段。

示例,操作 books 集合并传入如下文档作为参数:

{ "_id" : 11, "item" : "Divine Comedy", "stock" : 2 }

下面的操作会在文档中增加 price 字段并把 stock 的值加 5

db.books.update(
   { item: "Divine Comedy" },
   {
      $set: { price: 18 },
      $inc: { stock: 5 }
   }
)

被更新过的文档如下:

{ "_id" : 11, "item" : "Divine Comedy", "price" : 18, "stock" : 7 }

更新子文档的字段

使用点号分隔法( dot notation )更新子文档中的字段。

示例,操作 books 集合并传入如下文档作为参数:

{ _id: 50, item: "TDB", stock: 0, isbn: { group: 11, publisher: 1111, title: 11, digit: 1 } }

下面的操作会更新 isbn 子文档中的 publisherdigit 字段:

db.books.update(
   { _id: 50 },
   { $set: { "isbn.publisher": 2222, "isbn.digit": 0 } }
)

删除字段

下面的操作使用 $unset 操作符删除 stock 字段:

db.books.update( { _id: 11 }, { $unset: { stock: 1 } } )

 

替换全部字段

books 集合中传入如下文档:

{
    "_id" : 22,
    "item" : "The Banquet",
    "author" : "Dante",
    "price" : 20,
    "stock" : 4
}

下面的操作给 <update> 参数传入一个只包含键值对的文档,表示用新的文档替换原有文档中的所有字段。这个操作 不会 替换 _id 的值。如果给 <query> 参数和 <update> 参数传入的文档的键和值完全相同,就表示没有需要修改的字段:

db.books.update(
   { item: "The Banquet" },
   { item: "The Banquet", price: 19 , stock: 3 }
)

这个操作会创建如下新文档。这个操作会删除 author 字段并修改 pricestock 字段的值:

{
    "_id" : 22,
    "item" : "The Banquet",
    "price" : 19,
    "stock" : 3
}

 

如果没有匹配的记录,插入一个新的文档记录

下面这个操作把更新插( upsert )选项设置成 true ,所以 update() 操作在 books 集合中找不到能匹配 <query> 参数的文档时会创建一个新的文档记录。

db.books.update(
   { item: "The New Life" },
   { item: "The New Life", author: "Dante", price: 15 },
   { upsert: true }
)

如果没有能匹配上 <query> 参数的文档, update 操作会用 <update> 参数传入的的文档参数创建一个新的记录,并给这个条记录的 _id 字段一个“ObjectId“ 类型的唯一值:

{
    "_id" : ObjectId("51e5990c95098ed69d4a89f2"),
    "author" : "Dante",
    "item" : "The New Life",
    "price" : 15
}

 

批量更新文档记录

使用批量更新文档记录需要把 multi 选项设置成 true 。例如,下面的操作会更新所有 stock 小于 5 的文档记录:

db.books.update(
   { stock: { $lt: 5 } },
   { $set: { reorder: true } },
   { multi: true }
)

 

覆盖默认的写确认级别

如果在一个副本集中把写确认级别( write concern )设置成 "w: majority" 并设置一个 5000 毫秒的 “超时时间” , 此操作会在副本集中的多数成员已经执行完操作或等待时间超过5秒时返回。

db.books.update(
   { stock: { $lt: 5 } },
   { $set: { reorder: true } },
   {
     multi: true,
     writeConcern: { w: "majority", wtimeout: 5000 }
   }
)

联合使用 upsertupsert 选项:

创建一个包含如下文档记录的 books 集合:

{ _id: 11, author: "Dante", item: "Divine Comedy", price: 18, translatedBy: "abc123" }
{ _id: 12, author: "Dante", item: "Divine Comedy", price: 21, translatedBy: "jkl123" }
{ _id: 13, author: "Dante", item: "Divine Comedy", price: 15, translatedBy: "xyz123" }

命令中设置了 multi 参数来更新所有 item 字段值为 "Divine Comedy" 并且 author 字段值为 "Dante" 的记录。命令中设置 upsert: true ,当按上述条件匹配不到记录时会创建一个新的文档记录:

db.books.update(
   { item: "Divine Comedy", author: "Dante" },
   { $set: { reorder: false, price: 10 } },
   { upsert: true, multi: true }
)

下面的操作会更新所有匹配上的文档记录:

{ _id: 11, author: "Dante", item: "Divine Comedy", price: 10, translatedBy: "abc123", reorder: false }
{ _id: 12, author: "Dante", item: "Divine Comedy", price: 10, translatedBy: "jkl123", reorder: false }
{ _id: 13, author: "Dante", item: "Divine Comedy", price: 10, translatedBy: "xyz123", reorder: false }

如果集合中 没有 匹配到的记录,操作会变成插入一条记录:

{ _id: ObjectId("536aa66422363a21bc16bfd7"), author: "Dante", item: "Divine Comedy", reorder: false, price: 10 }

更新数组

更新指定位置的元素

如果想要更新一个数组字段中的一元素, update() 操作可以使用点号分隔法( dot notation )更新指定位置的数组元素。数组在mongodb中是最底层的数据类型。

下面的操作匹配 bios collection 集合中第一个 _id 字段的值等于 1 的文档记录,并更新数组字段 contribs 中的第二个元素:

db.bios.update(
   { _id: 1 },
   { $set: { "contribs.1": "ALGOL 58" } }
)

更新一个未知位置的元素

如果不知道数组中的位置, update() 操作中可以使用位置操作符 $ 。为了定位要更新数组元素,这个数组字段必须出现在 <query> 参数中。

下面的操作在 bios collection 集合中查找第一个 _id 等于 3 并且 contribs 数组字段中包含 compiler 元素的文档记录。如果找到, update() 操作把文档中第一个匹配到的元素更新成 A compiler

db.bios.update(
   { _id: 3, "contribs": "compiler" },
   { $set: { "contribs.$": "A compiler" } }
)

更新一个文档元素

The update() method can perform the update of an array that contains embedded documents by using the positional operator (i.e. $) and the dot notation.

下面这个例子查找 bios collection 集合中第一个 _id 字段等于 4awards 数组字段中嵌入文档的 by 字段等于 ACM 的文档记录。如果找到, update() 方法会更新第一个匹配到的嵌入文档中的 by 字段 :

db.bios.update(
   { _id: 4, "awards.by": "ACM"  } ,
   { $set: { "awards.$.by": "Association for Computing Machinery" } }
)

添加一个元素

下面的操作查找 bios collection 集合中第一个 _id 等于 1 的文档记录并在 awards 数组字段中添加一个内嵌文档:

db.bios.update(
   { _id: 1 },
   {
     $push: { awards: { award: "IBM Fellow", year: 1963, by: "IBM" } }
   }
)

下一个例子,使用更新操作符( $set )和点号分隔法 dot notation 访问 name 嵌入文档中的 middle 字段。使用追加操作符 $push$push operator )在 awards 数组中添加一个嵌入文档。

思考下面的操作:

db.bios.update(
   { _id: 1 },
   {
      $set: { "name.middle": "Warner" },
      $push: { awards: {
         award: "IBM Fellow",
            year: "1963",
            by: "IBM"
         }
      }
   }
)

这个 update() 操作:

  • 修改 name 字段中包含子文档的记录。更新操作符( $set )修改 name 文档中的 middle 字段。使用点号分隔法( dot notation )访问内嵌文档中的字段。

  • awards 数字字段中添加一个元素。追加操作符( $push )会在 awards“字段中添加一个文档作为新元素。

 

操作结果

在 2.6 版更改.

操作成功

The update() method returns a WriteResult object that contains the status of the operation. Upon success, the WriteResult object contains the number of documents that matched the query condition, the number of documents inserted by the update, and the number of documents modified:

WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

写确认异常

如果 update() 操作遇到“写确认异常”,返回的结果中会包 WriteResult.writeConcernError 字段:

WriteResult({
   "nMatched" : 1,
   "nUpserted" : 0,
   "nModified" : 1,
   "writeConcernError" : {
      "code" : 64,
      "errmsg" : "waiting for replication timed out at shard-a"
   }
})

与写确认无关的异常

如果 update() 操作遇到一个与写确认无关的异常,返回结果中会包含 WriteResult.writeError 字段:

WriteResult({
   "nMatched" : 0,
   "nUpserted" : 0,
   "nModified" : 0,
   "writeError" : {
      "code" : 7,
      "errmsg" : "could not contact primary for replica set shard-a"
   }
})

 

MongoDB 系列教程索引 : MongoDB 教程索引 (附有视频)

上一篇:  MongoDB 教程三: 高级查询 (SQL到MongoDB映射表)

下一篇: MongoDB 教程番外篇之管理工具: Rockmongo

本文:MongoDB 教程四: 高级更改操作

Leave a Reply