浅尝辄止MongoDB:操作(2)

目录

4. 更新数据


        大部分摘自《MongoDB大数据处理权威指南》(第3版)。 

4. 更新数据

(1)update()
        在MongoDB中可以使用update()函数执行数据更新操作。该函数将接受3个主要参数:criteria、objNew和option。参数criteria可用于指定一个查询,该查询选择将要更新的目标记录。使用objNew参数指定更新信息,也可以使用操作符来完成。参数option用于指定更新文档时的选项,它的可选值有upsert和multi。通过选项upsert可以指定该更新是否是upsert操作——它将告诉MongoDB,如果数据存在就更新,否则就创建数据。最后,通过选项multi可以指定是否应该更新所有匹配的文档,或者只更新第一个文档(默认行为)。

> db.media.update( { "Title" : "Matrix, The"}, {"Type" : "DVD", "Title" : "Matrix, The", "Released" : 1999, "Genre" : "Action"}, { upsert: true} );
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
>

        该例复写了集合中的文档,并为之保存更新后的值。注意,不带$set操作符update()函数会移除任何忽略的键。如上面的语句执行后,Cast键已经被移除:

> db.media.find( { "Title" : "Matrix, The"});
{ "_id" : ObjectId("5bac6025fc59dd405445ee0d"), "Type" : "DVD", "Title" : "Matrix, The", "Released" : 1999, "Genre" : "Action" }
>

        修改单条记录还可以使用updateOne函数,该函数与update的主要区别有两点:

  1. 不移除忽略的键。
  2. 必须使用$set操作符。
> db.media.find( { "Title" : "Matrix, The"});
{ "_id" : ObjectId("5bac6025fc59dd405445ee0d"), "Type" : "DVD", "Title" : "Matrix, The", "Released" : 1999, "Genre" : "Action" }
> db.media.updateOne( { "Title" : "Matrix, The"}, {"Type" : "DVD"}, { upsert: true} );
2018-09-27T13:31:54.772+0800 E QUERY    [js] Error: the update operation document must contain atomic operators :
DBCollection.prototype.updateOne@src/mongo/shell/crud_api.js:542:1
@(shell):1:1
> db.media.updateOne( { "Title" : "Matrix, The"}, {$set:{"Type" : "DVD"}}, { upsert: true} );
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 0 }
> db.media.updateOne( { "Title" : "Matrix, The"}, {$set:{"Type" : "DVD1"}}, { upsert: true} );
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.media.find( { "Title" : "Matrix, The"});
{ "_id" : ObjectId("5bac6025fc59dd405445ee0d"), "Type" : "DVD1", "Title" : "Matrix, The", "Released" : 1999, "Genre" : "Action" }
> db.media.updateOne( { "Title" : "Matrix, The"}, {$set:{"Type" : "DVD"}}, { upsert: true} );
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.media.find( { "Title" : "Matrix, The"});
{ "_id" : ObjectId("5bac6025fc59dd405445ee0d"), "Type" : "DVD", "Title" : "Matrix, The", "Released" : 1999, "Genre" : "Action" }
>

        如果要修改多条记录也有两种写法:

db.media.updateMany( { "Title" : "Matrix, The"}, {$set:{"Type" : "DVD"}}, { upsert: true} );

        或者:

db.media.update( { "Title" : "Matrix, The"}, {$set:{"Type" : "DVD", "Title" : "Matrix, The", "Released" : 1999, "Genre" : "Action"}}, { upsert: true,multi:true} );

(2)save()
        可以使用save()命令执行upsert。如果不指定_id值,save()执行一个插入操作,否则执行upsert操作。

> db.products.save( { item: "book", qty: 40 } );
WriteResult({ "nInserted" : 1 })
> db.products.find();
{ "_id" : ObjectId("5bac6f41845a6b94a74d82f0"), "item" : "book", "qty" : 40 }
> db.products.save( { _id: 100, item: "water", qty: 30 } );
WriteResult({ "nMatched" : 0, "nUpserted" : 1, "nModified" : 0, "_id" : 100 })
> db.products.find();
{ "_id" : ObjectId("5bac6f41845a6b94a74d82f0"), "item" : "book", "qty" : 40 }
{ "_id" : 100, "item" : "water", "qty" : 30 }
> db.products.save( { _id : 100, item : "juice" } );
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.products.find();
{ "_id" : ObjectId("5bac6f41845a6b94a74d82f0"), "item" : "book", "qty" : 40 }
{ "_id" : 100, "item" : "juice" }
>

(3)自动更新

  • $inc

        操作符$inc可以为指定的键执行原子更新操作,如果字段存在,就将该值增加指定的增量,否则创建该键。

> manga = ( { "Type" : "Manga", "Title" : "One Piece", "Volumes" : 612, "Read" : 520 } );
{ "Type" : "Manga", "Title" : "One Piece", "Volumes" : 612, "Read" : 520 }
> db.media.insertOne(manga);
{
    "acknowledged" : true,
    "insertedId" : ObjectId("5bac706d845a6b94a74d82f1")
}
> db.media.updateOne ( { "Title" : "One Piece"}, {$inc: {"Read" : 4} } );
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.media.find ( { "Title" : "One Piece" } );
{ "_id" : ObjectId("5bac706d845a6b94a74d82f1"), "Type" : "Manga", "Title" : "One Piece", "Volumes" : 612, "Read" : 524 }
>
  • $set

        可以使用$set操作符将某些字段设置为指定值。该操作符可以更新任何数据。

> db.media.update ( { "Title" : "Matrix, The" }, {$set : { Genre : "Sci-Fi" , Type : "DVD"} } );
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
>
  • $unset

        通过$unset操作符可以删除指定的字段。

> db.media.find( { "Title" : "Matrix, The"});
{ "_id" : ObjectId("5bac6025fc59dd405445ee0d"), "Type" : "DVD", "Title" : "Matrix, The", "Released" : 1999, "Genre" : "Sci-Fi" }
{ "_id" : ObjectId("5bac6c6c845a6b94a74d82ee"), "Title" : "Matrix, The" }
> db.media.updateOne ( {"Title": "Matrix, The"}, {$unset : { "Genre" : 1 } } );
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.media.find( { "Title" : "Matrix, The"});
{ "_id" : ObjectId("5bac6025fc59dd405445ee0d"), "Type" : "DVD", "Title" : "Matrix, The", "Released" : 1999 }
{ "_id" : ObjectId("5bac6c6c845a6b94a74d82ee"), "Title" : "Matrix, The" }
>
  • $push

        通过$push操作符可以在指定字段中添加某个值。如果该字段是个数组,那么该值将被添加到数组中。如果该字段尚不存在,那么该字段的值将被设置为数组。如果该字段存在,但不是数组,那么将会抛出错误。

> db.media.updateOne ( {"ISBN" : "978-1-4842-1183-0"}, {$push: { Author : "Griffin, Stewie"} } );
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.media.updateOne ( {"ISBN" : "978-1-4842-1183-0"}, {$push: { Title : "This isn't an array"} } );
2018-09-27T14:20:46.314+0800 E QUERY    [js] WriteError: The field 'Title' must be an array but is of type string in document {_id: ObjectId('5baae32464e6602b766d94c0')} :
WriteError({
    "index" : 0,
    "code" : 2,
    "errmsg" : "The field 'Title' must be an array but is of type string in document {_id: ObjectId('5baae32464e6602b766d94c0')}",
    "op" : {
        "q" : {
            "ISBN" : "978-1-4842-1183-0"
        },
        "u" : {
            "$push" : {
                "Title" : "This isn't an array"
            }
        },
        "multi" : false,
        "upsert" : false
    }
})
WriteError@src/mongo/shell/bulk_api.js:461:48
Bulk/mergeBatchResults@src/mongo/shell/bulk_api.js:841:49
Bulk/executeBatch@src/mongo/shell/bulk_api.js:906:13
Bulk/this.execute@src/mongo/shell/bulk_api.js:1150:21
DBCollection.prototype.updateOne@src/mongo/shell/crud_api.js:572:17
@(shell):1:1
> db.media.find ( { "ISBN" : "978-1-4842-1183-0" } );
{ "_id" : ObjectId("5baae32464e6602b766d94c0"), "Type" : "Book", "Title" : "Definitive Guide to MongoDB 3rd ed., The", "ISBN" : "978-1-4842-1183-0", "Publisher" : "Apress", "Author" : [ "Hows, David", "Plugge, Eelco", "Membrey, Peter", "Hawkins, Tim", "Griffin, Stewie" ] }
{ "_id" : ObjectId("5baae32864e6602b766d94c1"), "Type" : "Book", "Title" : "Definitive Guide to MongoDB 3rd ed., The", "ISBN" : "978-1-4842-1183-0", "Publisher" : "Apress", "Author" : [ "Hows, David", "Plugge, Eelco", "Membrey, Peter", "Hawkins, Tim" ] }
>
  • $each

        给数组添加几个值:

> db.media.updateOne( { "ISBN" : "978-1-4842-1183-0" }, { $push: { Author : { $each:
... ["Griffin, Peter", "Griffin, Brian"] } } } );
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.media.findOne ( { "ISBN" : "978-1-4842-1183-0" } );
{
    "_id" : ObjectId("5baae32464e6602b766d94c0"),
    "Type" : "Book",
    "Title" : "Definitive Guide to MongoDB 3rd ed., The",
    "ISBN" : "978-1-4842-1183-0",
    "Publisher" : "Apress",
    "Author" : [
        "Hows, David",
        "Plugge, Eelco",
        "Membrey, Peter",
        "Hawkins, Tim",
        "Griffin, Stewie",
        "Griffin, Peter",
        "Griffin, Brian"
    ]
}
>

        在使用$each时还可以使用$slice操作符。通过这种方式可以限制$push操作符中数组内元素的数量。$slice接受负数或0。使用负数将保证数组中的最后n个元素会保留,而使用0则表示清空数组。注意,操作符$slice必须是$push操作中的第一个修改操作符:

> db.media.updateOne( { "ISBN" : "978-1-4842-1183-0" }, { $push: { Author : { $each:
... ["Griffin, Meg", "Griffin, Louis"], $slice: -2 } } } );
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.media.findOne ( { "ISBN" : "978-1-4842-1183-0" } );
{
    "_id" : ObjectId("5baae32464e6602b766d94c0"),
    "Type" : "Book",
    "Title" : "Definitive Guide to MongoDB 3rd ed., The",
    "ISBN" : "978-1-4842-1183-0",
    "Publisher" : "Apress",
    "Author" : [
        "Griffin, Meg",
        "Griffin, Louis"
    ]
}
>
  • $addToSet

        操作符$addToSet是另一个可用于向数组中添加数据的命令。不过,只有数据不存在的时候,该操作符才能将数据添加到数组中。它的工作方式与$push不同。

> db.media.updateOne( { "ISBN" : "978-1-4842-1183-0" }, {$addToSet : { Author : "Griffin, Brian" } } );
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.media.findOne ( { "ISBN" : "978-1-4842-1183-0" } );
{
    "_id" : ObjectId("5baae32464e6602b766d94c0"),
    "Type" : "Book",
    "Title" : "Definitive Guide to MongoDB 3rd ed., The",
    "ISBN" : "978-1-4842-1183-0",
    "Publisher" : "Apress",
    "Author" : [
        "Griffin, Meg",
        "Griffin, Louis",
        "Griffin, Brian"
    ]
}
>

(4)从数组中删除元素

  • $pop

        删除数组中第一个或最后一个元素:

> db.media.updateOne( { "ISBN" : "978-1-4842-1183-0" }, {$pop : {Author : 1 } } );
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.media.findOne ( { "ISBN" : "978-1-4842-1183-0" } );
{
    "_id" : ObjectId("5baae32464e6602b766d94c0"),
    "Type" : "Book",
    "Title" : "Definitive Guide to MongoDB 3rd ed., The",
    "ISBN" : "978-1-4842-1183-0",
    "Publisher" : "Apress",
    "Author" : [
        "Griffin, Meg",
        "Griffin, Louis"
    ]
}
> db.media.updateOne( { "ISBN" : "978-1-4842-1183-0" }, {$pop : {Author : -1 } } );
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.media.findOne ( { "ISBN" : "978-1-4842-1183-0" } );
{
    "_id" : ObjectId("5baae32464e6602b766d94c0"),
    "Type" : "Book",
    "Title" : "Definitive Guide to MongoDB 3rd ed., The",
    "ISBN" : "978-1-4842-1183-0",
    "Publisher" : "Apress",
    "Author" : [
        "Griffin, Louis"
    ]
}
>
  • $pull

        删除所有指定值:

> db.media.updateOne ( {"ISBN" : "978-1-4842-1183-0"}, {$push: { Author : "Griffin, Stewie"} } );
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.media.findOne ( { "ISBN" : "978-1-4842-1183-0" } );
{
    "_id" : ObjectId("5baae32464e6602b766d94c0"),
    "Type" : "Book",
    "Title" : "Definitive Guide to MongoDB 3rd ed., The",
    "ISBN" : "978-1-4842-1183-0",
    "Publisher" : "Apress",
    "Author" : [
        "Griffin, Louis",
        "Griffin, Stewie"
    ]
}
> db.media.updateOne ( {"ISBN" : "978-1-4842-1183-0"}, {$pull : { Author : "Griffin, Stewie" } } );
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.media.findOne ( { "ISBN" : "978-1-4842-1183-0" } );
{
    "_id" : ObjectId("5baae32464e6602b766d94c0"),
    "Type" : "Book",
    "Title" : "Definitive Guide to MongoDB 3rd ed., The",
    "ISBN" : "978-1-4842-1183-0",
    "Publisher" : "Apress",
    "Author" : [
        "Griffin, Louis"
    ]
}
>
  • $pullAll

        删除数组中的多个元素:

> db.media.updateOne( { "ISBN" : "978-1-4842-1183-0"}, {$pullAll : { Author : ["Griffin, Louis","Griffin, Peter","Griffin, Brian"] } } );
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.media.findOne ( { "ISBN" : "978-1-4842-1183-0" } );
{
    "_id" : ObjectId("5baae32464e6602b766d94c0"),
    "Type" : "Book",
    "Title" : "Definitive Guide to MongoDB 3rd ed., The",
    "ISBN" : "978-1-4842-1183-0",
    "Publisher" : "Apress",
    "Author" : [ ]
}
>

(5)指定匹配数组的位置
        可以在查询中使用$操作符指定查询中匹配数组元素的位置。该操作符可用于在搜索到一个数组元素之后,对它进行数据操作。

> db.media.updateOne( { "Artist" : "Nirvana" }, {$addToSet : { Tracklist : {"Track" : 2,"Title": "Been a Son", "Length":"2:23"} } } );
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.media.findOne ( { "Tracklist.Title" : "Been a Son"});
{
    "_id" : ObjectId("5baae67464e6602b766d94c3"),
    "Type" : "CD",
    "Artist" : "Nirvana",
    "Title" : "Nevermind",
    "Tracklist" : [
        {
            "Track" : 2,
            "Title" : "Been a Son",
            "Length" : "2:23"
        }
    ]
}
> db.media.updateOne( { "Tracklist.Title" : "Been a Son"}, {$inc:{"Tracklist.$.Track" : 1} } );
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.media.findOne ( { "Tracklist.Title" : "Been a Son"});
{
    "_id" : ObjectId("5baae67464e6602b766d94c3"),
    "Type" : "CD",
    "Artist" : "Nirvana",
    "Title" : "Nevermind",
    "Tracklist" : [
        {
            "Track" : 3,
            "Title" : "Been a Son",
            "Length" : "2:23"
        }
    ]
}
>

(6)原子操作
        MongoDB支持对单个文档的原子操作,不支持在单个操作中以原子方式更新多个文档。如果一组操作满足下面的条件,那就可以称它们为原子操作:

  • 其它进程无法获得修改结果,除非整租操作都已完成。
  • 如果其中一个操作失败,整个原子操作都将失败,并全部回滚,数据将被恢复至运行原子操作之前的状态。

        执行原子操作时的标准行为是锁定数据,不允许其它查询访问,但MongoDB不支持锁或复杂的事务。MongoDB包含的几种更新操作都可以原子操作的方式更新数据:

  • $set:设置特定值。
  • $unset:删除特定值。
  • $inc:将某个值增大特定的量。
  • $push:向数组中添加值。
  • $pull:从现有数组中删除单个值。
  • $pullAll:从现有数组中删多个值。

        使用Update if Current方法
        另一个更新数据的策略是使用Update if Current(如果数据目前仍未改变就更新)方法。该方法有3个步骤,所有步骤都是以原子的方式完成:

  1. 从文档中取得对象。
  2. 在本地修改对象。
  3. 发送更新请求更新对象值,假定当前值仍然匹配之前取得的值。

        该方法本质上是一种乐观锁定的实现。为了避免并发情况下的ABA问题,可以使用下面的方法:

  • 在更新的查询表达式中使用完整的对象,而不是只使用_id和comments.by字段。
  • 使用$set更新重要的字段。即使其它字段已经改变,也不会受该字段的影响。
  • 在对象中添加一个版本变量,并在每次更新时增加它的值。
  • 如果可能,使用$操作符,而不是Update-if-Current序列操作。

        还可以通过执行findAndModify命令来实现对文档的原子操作。该命令将修改并返回文档。它接受3个主要操作符:<query>用于指定目标文档;<sort>用于对多个匹配文档进行排序;<operations>用于指定希望执行的操作。例如:

> db.media.findAndModify( { "Title" : "One Piece",sort:{"Title": -1}, remove: true} );
{
    "_id" : ObjectId("5bab30521062c31f5bdf664b"),
    "Type" : "DVD",
    "Title" : "Toy Story 3",
    "Released" : 2010
}
>

        这段代码返回匹配搜索条件的文档,找到并删除Title为'One Piece'的第一个文档。下面的例子将修改文档而不是删除:

> db.media.findAndModify( { query: { "ISBN" : "978-1-4842-1183-0" }, sort: {"Title":-1},update: {$set: {"Title" : " Different Title"} } } );
{
    "_id" : ObjectId("5baae32464e6602b766d94c0"),
    "Type" : "Book",
    "Title" : "Definitive Guide to MongoDB 3rd ed., The",
    "ISBN" : "978-1-4842-1183-0",
    "Publisher" : "Apress",
    "Author" : [ ]
}
>

        如果希望返回更新后的文档,可以在查询后添加new操作符:

> db.media.findAndModify( { query: { "ISBN" : "978-1-4842-1183-0" }, sort: {"Title":-1},update: {$set: {"Title" : " Different Title"} }, new:true } );
{
    "_id" : ObjectId("5baae32864e6602b766d94c1"),
    "Type" : "Book",
    "Title" : " Different Title",
    "ISBN" : "978-1-4842-1183-0",
    "Publisher" : "Apress",
    "Author" : [
        "Hows, David",
        "Plugge, Eelco",
        "Membrey, Peter",
        "Hawkins, Tim"
    ]
}
>

        该命令中可以使用任何修改操作符,而不只是$set。

©️2020 CSDN 皮肤主题: 深蓝海洋 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值