MongoDB 更新篇

来源:互联网 发布:centos telnet 命令 编辑:程序博客网 时间:2024/06/16 02:16

上一篇文档,我们说了基本的MongoDB命令。接下来,我们要重点写一下,更新的操作。MongoDB的更新非常多样灵活,基本上可以应对日常上大部分的使用场景,下面我将会一一细说。


1、基本的文档替换更新

直接通过update方法更新整份文档

db.product.update({product_name:"iPhoneX"},{product_name:"iPhoneX",price:9100,description:"一台贵到666的手机",product_number:"9830829131",brand:"苹果"});


2、$inc 修改器

刚刚那个商品记录,其实我们只希望调整价格而已。但是我们却需要更新一份文档,事实我们可以使用$inc修改器去调整价格

db.product.update({product_name:"iPhoneX"},{$inc:{"price":500}});

通过$inc 修改器为price属性字段增加500块,当然如果我们需要减价199也可以使用负数进行递减:

db.product.update({product_name:"iPhoneX"},{$inc:{price:-199}});


3、$set修改器

$set修改器可以针对文档中某个字段进行修改,例如我们可以修改刚刚那个文档的brand,我们改成Apple

db.product.update({product_name:"iPhoneX"},{$set:{"brand":"Apple"}});

其实我们还可以通过$set修改器,添加新的字段:

db.product.update({product_name:"iPhoneX"},{$set:{"size":"143.6 X 70.9"}});

修改结果如下:

{     "_id" :ObjectId("59e47fdb203e071a1b02e544"),     "product_name" : "iPhoneX",     "price" :9499.0,     "description" :"一台贵到666的手机",     "product_number" : "9830829131",     "brand" :"Apple",     "size" :"143.6 X 70.9"}

当然我们也可以设置内嵌的文档,例如我希望添加最近一条的评论。

var newComment = {comment_content:"TEST NEW COMMENT",user_id:109382,create_date:new Date()};db.product.update({product_name:"iPhoneX"},{$set:{newest_commment:newComment}});db.product.findOne();db.product.update({product_name:"iPhoneX"},{$set:{"newest_commment.comment_content":"this is newtest comment at that place!"}});db.product.findOne();
需要注意的是,之前我一直没有再key当中打双引号是不规范的,如果需要调用内嵌文档(内嵌对象)的时候必须要在key写上双引号,不然无法执行。

结果如下:

{     "_id" : ObjectId("59e47fdb203e071a1b02e544"),     "product_name" : "iPhoneX",     "price" : 9499.0,     "description" : "一台贵到666的手机",     "product_number" : "9830829131",     "brand" : "Apple",     "size" : "143.6 X 70.9",     "newest_commment" : {        "comment_content" : "this is newtest comment at that place!",         "user_id" : 109382.0,         "create_date" : ISODate("2017-10-17T01:15:20.466+0000")    }}


4、数组修改器

我们希望在商品上添加规格参数,我们可以数组的方式对规格对象进行文档内嵌。但是规格会有多个,我们可以在文档中通过数组类型进行存储

我们可以使用$push 为文档中的数组属性的结尾添加一个新的item,如果$push的属性不存在,就会创建一个数组并添加你要添加的数据:

var iphonexSku64GSilver={capacity:"64G",style:"silver"};var iphonexSku64GGray={capacity:"64G",style:"gray"};db.product.update({product_name:"iPhoneX"},{$push:{sku:iphonexSku64GSilver}});db.product.update({product_name:"iPhoneX"},{$push:{sku:iphonexSku64GGray}});db.product.findOne();
结果:

{     "_id" : ObjectId("59e47fdb203e071a1b02e544"),     "product_name" : "iPhoneX",     "price" : 9300.0,     "description" : "一台贵到666的手机",     "product_number" : "9830829131",     "brand" : "Apple",     "size" : "143.6 X 70.9",     "newest_commment" : {        "comment_content" : "this is newtest comment at that place!",         "user_id" : 109382.0,         "create_date" : ISODate("2017-10-17T01:15:20.466+0000")    },     "sku" : [        {            "capacity" : "64G",             "style" : "silver"        },         {            "capacity" : "64G",             "style" : "gray"        }    ]}
如果我们需要连续添加两个item的话,我们还需要借助$each这个子操作符

下面我们就通过$push 和 $each 配合连续添加连个SKU,这次是256G的

db.product.update({product_name:"iPhoneX"},{$push:{sku:{$each:[iphonexSku256GSilver,iphonexSku256GGray]}}});db.product.findOne();
结果如下:

{     "_id" : ObjectId("59e47fdb203e071a1b02e544"),     "product_name" : "iPhoneX",     "price" : 9300.0,     "description" : "一台贵到666的手机",     "product_number" : "9830829131",     "brand" : "Apple",     "size" : "143.6 X 70.9",     "newest_commment" : {        "comment_content" : "this is newtest comment at that place!",         "user_id" : 109382.0,         "create_date" : ISODate("2017-10-17T01:15:20.466+0000")    },     "sku" : [        {            "capacity" : "64G",             "style" : "silver"        },         {            "capacity" : "64G",             "style" : "gray"        },         {            "capacity" : "256G",             "style" : "silver"        },         {            "capacity" : "256G",             "style" : "gray"        }    ]}
有时我们希望$each添加数组的时候,希望得到长度的限制,例如$each 里面有5个item,但是我只需要3个item。为了我越界,我们可以通过$slice保留最后3个item,而且我们还可以使用$sort对数组进行先排序然后再截取最后3个item。

var commentA = {content:"Comment A content !",user_id:399,popularity_degree:89};var commentB = {content:"Comment B content !",user_id:349,popularity_degree:39};var commentC = {content:"Comment C content !",user_id:119,popularity_degree:99};var commentD = {content:"Comment D content !",user_id:334,popularity_degree:76};var commentE = {content:"Comment E content !",user_id:893,popularity_degree:103};var commentArray = [commentA,commentB,commentC,commentD,commentE];db.product.update({product_name:"iPhoneX"},{$push:  {popular_comment:    {$each:commentArray,      $slice:-3,      $sort:{"popularity_degree":1}    }  } });db.product.findOne();
插入结果:

{     "_id" : ObjectId("59e47fdb203e071a1b02e544"),     "product_name" : "iPhoneX",     "price" : 9300.0,     "description" : "一台贵到666的手机",     "product_number" : "9830829131",     "brand" : "Apple",     "size" : "143.6 X 70.9",     "newest_commment" : {        "comment_content" : "this is newtest comment at that place!",         "user_id" : 109382.0,         "create_date" : ISODate("2017-10-17T01:15:20.466+0000")    },     "sku" : [        {            "capacity" : "64G",             "style" : "silver"        },         {            "capacity" : "64G",             "style" : "gray"        },         {            "capacity" : "256G",             "style" : "silver"        },         {            "capacity" : "256G",             "style" : "gray"        }    ],     "popular_comment" : [        {            "content" : "Comment A content !",             "user_id" : 399.0,             "popularity_degree" : 89.0        },         {            "content" : "Comment C content !",             "user_id" : 119.0,             "popularity_degree" : 99.0        },         {            "content" : "Comment E content !",             "user_id" : 893.0,             "popularity_degree" : 103.0        }    ]}

5、数组作为SET集合

通常我们也有使用SET集合的场景,例如keyword,不希望每次添加的商品关键字会有重复,这样不利于查询。但是MongoDB只有数组类型,所以我们需要借助$addToSet去完成这一任务。

db.product.update({product_name:"iPhoneX"},{$addToSet:{keyword:"iphone"}});db.product.findOne();db.product.update({product_name:"iPhoneX"},{$addToSet:{keyword:{$each:["iphone","苹果","apple"]}}});db.product.findOne();
结果:

{     "_id" : ObjectId("59e47fdb203e071a1b02e544"),     "product_name" : "iPhoneX",     "price" : 9300.0,     "description" : "一台贵到666的手机",     "product_number" : "9830829131",     "brand" : "Apple",     "size" : "143.6 X 70.9",     "newest_commment" : {        "comment_content" : "this is newtest comment at that place!",         "user_id" : 109382.0,         "create_date" : ISODate("2017-10-17T01:15:20.466+0000")    },     "sku" : [        {            "capacity" : "64G",             "style" : "silver"        },         {            "capacity" : "64G",             "style" : "gray"        },         {            "capacity" : "256G",             "style" : "silver"        },         {            "capacity" : "256G",             "style" : "gray"        }    ],     "popular_comment" : [        {            "content" : "Comment A content !",             "user_id" : 399.0,             "popularity_degree" : 89.0        },         {            "content" : "Comment C content !",             "user_id" : 119.0,             "popularity_degree" : 99.0        },         {            "content" : "Comment E content !",             "user_id" : 893.0,             "popularity_degree" : 103.0        }    ],     "keyword" : [        "iphone",         "苹果",         "apple"    ]}

6、删除数组元素

删除其中一个数组元素,我们可以通过$pop删除数组头部或者尾部的元素

db.product.update({product_name:"iPhoneX"},{$pop:{keyword:1}});db.product.update({product_name:"iPhoneX"},{$pop:{keyword:-1}});

value 为 1 的时候删除尾部第一个元素,value为-1时删除头部第一个元素。


7、基于位置的数组修改

我们也可以通过数组的下标去,修改数组中的元素。

db.product.update({product_name:"iPhoneX"},{$set:{"popular_comment.1.popularity_degree":108}});db.product.findOne();

上面就不用怎么解释了,就是修改popular_comment中的第二个元素的populartity_degree属性

结果:

{     "_id" : ObjectId("59e47fdb203e071a1b02e544"),     "product_name" : "iPhoneX",     "price" : 9300.0,     "description" : "一台贵到666的手机",     "product_number" : "9830829131",     "brand" : "Apple",     "size" : "143.6 X 70.9",     "newest_commment" : {        "comment_content" : "this is newtest comment at that place!",         "user_id" : 109382.0,         "create_date" : ISODate("2017-10-17T01:15:20.466+0000")    },     "sku" : [        {            "capacity" : "64G",             "style" : "silver"        },         {            "capacity" : "64G",             "style" : "gray"        },         {            "capacity" : "256G",             "style" : "silver"        },         {            "capacity" : "256G",             "style" : "gray"        }    ],     "popular_comment" : [        {            "content" : "Comment A content !",             "user_id" : 399.0,             "popularity_degree" : 89.0        },         {            "content" : "Comment C content !",             "user_id" : 119.0,             "popularity_degree" : 108.0        },         {            "content" : "Comment E content !",             "user_id" : 893.0,             "popularity_degree" : 103.0        }    ],     "keyword" : [        "苹果",         "iphone",         "apple"    ]}


还有一种就是通过修改匹配条件的时候直接定位到要改的坐标,很难说的明白,看看例子:

db.product.update({"sku.capacity":"64G"},{$set:{"sku.$.price":8599}});db.product.findOne();
通过结果可以发现,只有第一个匹配的元素才会修改。事实上我们的匹配添加应该匹配到两个元素,但是实际修改只有匹配到第一个元素

{     "_id" : ObjectId("59e47fdb203e071a1b02e544"),     "product_name" : "iPhoneX",     "price" : 9300.0,     "description" : "一台贵到666的手机",     "product_number" : "9830829131",     "brand" : "Apple",     "size" : "143.6 X 70.9",     "newest_commment" : {        "comment_content" : "this is newtest comment at that place!",         "user_id" : 109382.0,         "create_date" : ISODate("2017-10-17T01:15:20.466+0000")    },     "sku" : [        {            "capacity" : "64G",             "style" : "silver",             "price" : 8599.0        },         {            "capacity" : "64G",             "style" : "gray"        },         {            "capacity" : "256G",             "style" : "silver"        },         {            "capacity" : "256G",             "style" : "gray"        }    ],     "popular_comment" : [        {            "content" : "Comment A content !",             "user_id" : 399.0,             "popularity_degree" : 89.0        },         {            "content" : "Comment C content !",             "user_id" : 119.0,             "popularity_degree" : 108.0        },         {            "content" : "Comment E content !",             "user_id" : 893.0,             "popularity_degree" : 103.0        }    ],     "keyword" : [        "苹果",         "iphone",         "apple"    ]}



9、修改数据导致文档大小经常改变,然而再导致大量的移动数据或者经常被打乱数据,使用usePowerOf2Size可以提高磁盘的服用率

db.runCommand({"collMod":product,"usePowerOf2Sizes":true});
启动之后块的大小都是2的幂,导致初始空间分配的时候没有那么高效了。原地更新集合或者插入会导致写入速度会变慢,所以只适合于文档大小经常被该表导致大量移动的collection。

这里我就把usePowerOf2Sizes禁用掉了。

db.runCommand({"collMod":product,"usePowerOf2Sizes":false});


10、upsert 更新

upsert是一种特殊的更新操作,如果没有找到调整匹配的文档,就会使用这个条件创建一个新的文档,将修改的数据插入到这个新建的文档当中。

我们这里记录一下product的访问记录,我们使用一个新的collection,product_vp。还是使用我们原来的update 方法,不过我们需要第三个参数,第三个参数我们写上true代表我们使用upsert更新

var iphonex = db.product.findOne({product_name:"iPhoneX"});db.product_pv.update({"product_id":iphonex._id},{$inc:{"pageviews":1}},true);db.product_pv.findOne();db.product_pv.update({"product_id":iphonex._id},{$inc:{"pageviews":1}},true);db.product_pv.findOne();
结果:

{     "_id" : ObjectId("59e59c198742e0735878414e"),     "product_id" : ObjectId("59e47fdb203e071a1b02e544"),     "pageviews" : 2.0}
还有一种操作是$setOnInsert,如果文档存在就不会插入或者修改。如果文档不存在就会插入这个属性

db.product_pv.update({},{$setOnInsert:{"create_at":new Date()}},true);db.product_pv.findOne();


11、save

这个跟JPA/hibernate 的save 非常相似,如果对象不存在就新建,如果存在就更新以下就是更新。

主要是判断对象是否存在_id字段的,如果有id就证明这个对象已经存在了,如果没有id就等于不存在,就会去创建一个新的。

iphonex.price = iphonex.sku[0].price;db.product.save(iphonex);db.product.findOne();

结果,我上面的代码同步对象中sku的价格和显示的价格

{     "_id" : ObjectId("59e47fdb203e071a1b02e544"),     "product_name" : "iPhoneX",     "price" : 8599.0,     "description" : "一台贵到666的手机",     "product_number" : "9830829131",     "brand" : "Apple",     "size" : "143.6 X 70.9",     "newest_commment" : {        "comment_content" : "this is newtest comment at that place!",         "user_id" : 109382.0,         "create_date" : ISODate("2017-10-17T01:15:20.466+0000")    },     "sku" : [        {            "capacity" : "64G",             "style" : "silver",             "price" : 8599.0        },         {            "capacity" : "64G",             "style" : "gray"        },         {            "capacity" : "256G",             "style" : "silver"        },         {            "capacity" : "256G",             "style" : "gray"        }    ],     "popular_comment" : [        {            "content" : "Comment A content !",             "user_id" : 399.0,             "popularity_degree" : 89.0        },         {            "content" : "Comment C content !",             "user_id" : 119.0,             "popularity_degree" : 108.0        },         {            "content" : "Comment E content !",             "user_id" : 893.0,             "popularity_degree" : 103.0        }    ],     "keyword" : [        "苹果",         "iphone",         "apple"    ]}


12、更新多个文档

我们在使用update方法的时候,事实上只会更新第一条匹配到条件的文档,其他匹配到的文档都不会更新,我们需要用到update方法当中的第四个参数

为了演示测试,我会多创建一个iPhoneX的商品,并且为所有商品添加店铺标识,代表不同店铺售卖的iPhone X。

//这是我们原来的iPhone Xvar iphonex = db.product.findOne({product_name:"iPhoneX"});iphonex.store = "官方自营店";db.product.save(iphonex);db.product.findOne();//新增第三方渠道的iPhone Xvar iphonexb = iphonex;iphonexb._id = ObjectId();db.product.insert(iphonexb);iphonexb.store = "第三方渠道";db.product.save(iphonexb);db.product.findOne({_id:iphonexb._id});

我们将平台上所有iPhone X的商品统一修改商品编码:

db.product.update({"product_name":"iPhoneX"},{$set:{"product_number":"9088816371"}},false,true);
可以见看到返回的结果显示,match两条记录,修改了两条记录

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


13、获得最后一次执行的文档结果

通常我们需要获得最后一次更新之后的文档数据,保证原子性。

所以我们这里可以用另外一个查询方法,findAndModity

db.runCommand(   {     findAndModify: "product",     query: { product_name: "iPhoneX" },     sort: { price: 1 },     update: { $inc: { price: 100 } },     upsert: false   } );

上面的代码,应该也比较清楚的findAndModify的value是collection的名字,query是要匹配的文档查询条件,sort是排序方式,update就是更新文档的操作,upsert就是不存在是否创建一个新的文档。

结果就会马上返回,不用再去调用findOne去查询:

{     "lastErrorObject" : {        "updatedExisting" : true,         "n" : 1.0    },     "value" : {        "_id" : ObjectId("59e5a20f7dfc75087c893b50"),         "product_name" : "iPhoneX",         "price" : 8599.0,         "description" : "一台贵到666的手机",         "product_number" : "9088816371",         "brand" : "Apple",         "size" : "143.6 X 70.9",         "newest_commment" : {            "comment_content" : "this is newtest comment at that place!",             "user_id" : 109382.0,             "create_date" : ISODate("2017-10-17T01:15:20.466+0000")        },         "sku" : [            {                "capacity" : "64G",                 "style" : "silver",                 "price" : 8599.0            },             {                "capacity" : "64G",                 "style" : "gray"            },             {                "capacity" : "256G",                 "style" : "silver"            },             {                "capacity" : "256G",                 "style" : "gray"            }        ],         "popular_comment" : [            {                "content" : "Comment A content !",                 "user_id" : 399.0,                 "popularity_degree" : 89.0            },             {                "content" : "Comment C content !",                 "user_id" : 119.0,                 "popularity_degree" : 108.0            },             {                "content" : "Comment E content !",                 "user_id" : 893.0,                 "popularity_degree" : 103.0            }        ],         "keyword" : [            "苹果",             "iphone",             "apple"        ],         "store" : "第三方渠道"    },     "ok" : 1.0}

还有非常多的属性可以去设置,我就不一一去说了,这里贴出一下官方文档的方法属性配置表:

FieldTypeDescriptionquerydocumentOptional. The selection criteria for the modification. The query field employs the same query selectors as used in the db.collection.find() method. Although the query may match multiple documents, findAndModifywill only select one document to modify.sortdocumentOptional. Determines which document the operation modifies if the query selects multiple documents. findAndModify modifies the first document in the sort order specified by this argument.removebooleanMust specify either the remove or the update field. Removes the document specified in the query field. Set this to true to remove the selected document . The default is false.updatedocumentMust specify either the remove or the update field. Performs an update of the selected document. The update field employs the same update operators or field: value specifications to modify the selected document.newbooleanOptional. When true, returns the modified document rather than the original. The findAndModify method ignores the new option for remove operations. The default is false.fieldsdocumentOptional. A subset of fields to return. The fieldsdocument specifies an inclusion of a field with 1, as in: fields: { <field1>: 1, <field2>: 1, ...}. See projection.upsertboolean

Optional. Used in conjuction with the update field.

When truefindAndModify() either:

  • Creates a new document if no documents match the query. For more details see upsert behavior.
  • Updates a single document that matches the query.

To avoid multiple upserts, ensure that the query fields are uniquely indexed.

Defaults to false.

bypassDocumentValidationboolean

Optional. Enables findAndModify to bypass document validation during the operation. This lets you update documents that do not meet the validation requirements.

New in version 3.2.

writeConcerndocument

Optional. A document expressing the write concern. Omit to use the default write concern.

New in version 3.2.

maxTimeMSintegerOptional. Specifies a time limit in milliseconds for processing the operation.findAndModifystringThe collection against which to run the command.collationdocument

Optional.

Specifies the collation to use for the operation.

Collation allows users to specify language-specific rules for string comparison, such as rules for lettercase and accent marks.

The collation option has the following syntax:

collation: {   locale: <string>,   caseLevel: <boolean>,   caseFirst: <string>,   strength: <int>,   numericOrdering: <boolean>,   alternate: <string>,   maxVariable: <string>,   backwards: <boolean>}

When specifying collation, the locale field is mandatory; all other collation fields are optional. For descriptions of the fields, see Collation Document.

If the collation is unspecified but the collection has a default collation (see db.createCollection()), the operation uses the collation specified for the collection.

If no collation is specified for the collection or for the operations, MongoDB uses the simple binary comparison used in prior versions for string comparisons.

You cannot specify multiple collations for an operation. For example, you cannot specify different collations per field, or if performing a find with a sort, you cannot use one collation for the find and another for the sort.

New in version 3.4.

Output

The findAndModify command returns a document with the following fields:

FieldTypeDescriptionvaluedocumentContains the command’s returned value. See value for details.lastErrorObjectdocumentContains information about updated documents. See lastErrorObject for details.oknumberContains the command’s execution status. 1 on success, or 0 if an error occurred.


原创粉丝点击