MongoDB:14-MongoDB- 原子操作

   
   
  1. Redis采用的是异步I/O非阻塞的单进程模型,每一条Redis命令都是原子性的。
  2. 那么mongoDB呢? mongo有哪些原子操作呢?有哪些实现事务性操作的技巧呢?

MongoDB 原子操作

  
  
  1. mongodb不支持事务,所以,在你的项目中应用时,要注意这点。无论什么设计,都不要要求mongodb保证数据的完整性
  2. 但是mongodb提供了许多原子操作,比如文档的保存,修改,删除等,都是原子操作。
  3. 所谓原子操作就是要么这个文档保存到Mongodb,要么没有保存到Mongodb,不会出现查询到的文档没有保存完整的情况。


原子操作数据模型

  • 考虑下面的例子,图书馆的书籍及结账信息。
  • 实例说明了在一个相同的文档中如何确保嵌入字段关联原子操作(update:更新)的字段是同步的
        
        
    1. book = {
    2. _id: 123456789,
    3. title: "MongoDB: The Definitive Guide",
    4. author: [ "Kristina Chodorow", "Mike Dirolf" ],
    5. published_date: ISODate("2010-09-24"),
    6. pages: 216,
    7. language: "English",
    8. publisher_id: "oreilly",
    9. available: 3,
    10. checkout: [ { by: "joe", date: ISODate("2012-10-15") } ]
    11. }

  • 你可以使用 db.collection.findAndModify() 方法来判断书籍是否可结算并更新新的结算信息
    • 在同一个文档中嵌入的 available 和 checkout 字段来确保这些字段是同步更新的:
  •     
        
    1. db.books.findAndModify ( {
    2. query: {
    3. _id: 123456789,
    4. available: { $gt: 0 }
    5. },
    6. update: {
    7. $inc: { available: -1 },
    8. $push: { checkout: { by: "abc", date: new Date() } }
    9. }
    10. } )
         
         
    1. 拥有类似事务特性的更新与查询操作——findAndModify.
    2. 它是原子性的,会返回符合查询条件的更新后的文档。
    3.          
               
      1. db.COLLECTION_NAME.findAndModify({query:{},
      2. update:{},
      3. remove:true|false,
      4. new:true|false,
      5. sort:{},
      6. fields:{},
      7. upsert:true|false});

      • query是查询选择器,与findOne的查询选择器相同
      • update是要更新的值,不能与remove同时出现
      • remove表示删除符合query条件的文档,不能与update同时出现
      • newtrue:返回个性后的文档,false:返回个性前的,默认是false
      • sort:排序条件,与sort函数的参数一致。
      • fields:投影操作,与find*的第二个参数一致。
      • upsert:与updateupsert参数一样。
    4. 不论是update的第二个参数,还是findAndModifyupdate,在不指定更新操作符的情况下,将会用指定的新值替换旧值。
    5. 比如,
    6.          
               
      1. use iteye;
      2. db.blog.update({_id:ObjectId('......')},{title:'new title'});

    7. //上面的操作就把指定_id的文档的标题改成了‘new title’
      • 如果指定了更新操作符,就可以实现更复杂灵活的更新操作。
      • 可以通过更新操作符,增加或减少数值,针对数组类型的属性,做类似队列或栈的操作
      • 单从这一点来说,mongo要比sql数据库强大的多了。


原子操作常用命令(更新操作符)

  • $set
    • 用来指定一个键并更新键值,若键不存在并创建。
             
             
      1.  用"$set"甚至可以修改键的数据类型
                   
                   
        1. db.users.insert({"name":"egger", "age": 28, "sex" : "male"})
        2. db.users.update({"_id" : ObjectId("51826852c75fdd1d8b805801")},{"$set" : {"sex" :1 }} )

             
             
      1. { $set : { field : value } }
                   
                   
        1. 使用"$set"修改内嵌文档:
                    
                    
        1. db.posts.update({"author.name":"egger"},{"$set":{"author.name":"mongo","author.age":18}})

  • $unset
    • 用来删除一个键。
             
             
      1. { $unset : { field : 1} }

  • $inc
    • $inc可以对文档的某个值为数字型(只能为满足要求的数字)的键进行增减的操作。
             
             
      1. 负数,表示减少
             
             
      1. "$inc"只能用于整数、长整数或双精度浮点数。要是用在其他类型的数据上就会导致操作失败。
             
             
      1. { $inc : { field : value } }
  • upsert
         
         
    1. upsert是一种特殊的更新操作,不是一个操作符。(upsert = up[date]+[in]sert
    2. update() 方法的三个参数是upsert,这个参数是个布尔类型,默认是false
    3. 当它为true的时候,update方法会首先查找与第一个参数匹配的记录,在用第二个参数更新之,
    4. 如果找不到与第一个参数匹配的的记录,
    5. 就会以这个条件和更新文档为基础创建一个新的文档。如果找到了匹配的文档,则正常更新。
    6. upsert非常方便,不必预置集合,同一方法可以既创建又更新文档。

  • $setOnInsert
         
         
    1. update方法使用upsert选项执行insert操作时,$setOnInsert操作符给相应的字段赋值。类似sqlupdate 语句的set
    2.          
               
      1. db.collection.update( <query>,
      2. { $setOnInsert: { <field1>: <value1>, ... } },
      3. { upsert: true } //{ upsert: true }可以用true替换
      4. )


  • $(query)
         
         
    1. $ (query)
    2.          
               
      1. 语法: { "<array>.$" : value }
      2.  当对数组字段进行更新时,且没有明确指定的元素在数组中的位置,
      3. 我们使用定位操作符("$")标识一个元素,数字都是以0开始的。
      4.   
      5. update()一起使用:

    3. 定位操作符("$")作为第一个匹配查询条件的元素的占位符,也就是在数组中的索引值。
    4. 数组字段必须出现查询文档中。
    5.   集合students中有两条文档:
               
               
      1. { "_id" : 1, "grades" : [ 78, 88, 88 ] }
      2. { "_id" : 2, "grades" : [ 88, 90, 92 ] }
    6.   执行下列语句创建集合文档数据:
               
               
      1. db.students.remove();
      2. db.students.insert({ "_id" : 1, "grades" : [ 78, 88, 88 ] });
      3. db.students.insert({ "_id" : 2, "grades" : [ 88, 90, 92 ] });

    7.   执行下列操作:
    8. //查询匹配的文档中,数组有2个88,只更新第一个匹配的元素,也就是"grades.1"
    9.          
               
      1. db.students.update( { _id: 1, grades: 88 }, { $set: { "grades.$" : 82 } }) ;

    10. //查询文档中没有出现grades字段,查询报错
    11.          
               
      1. db.students.update( { _id: 2 }, { $set: { "grades.$" : 82 } } );


  • $push
    • 用法:
             
             
      1. { $push : { field : value } }
      2.            
                   
        1. value追加到field里面去,field一定要是数组类型才行,如果field不存在,会新增一个数组类型加进去。
  • $pushAll
    • 同$push,只是一次可以追加多个值到一个数组字段内。
             
             
      1. { $pushAll : { field : value_array } }

  • $pull
    • 从数组field内删除一个等于value值。
             
             
      1. 语法:db.collection.update( { field: <query> }, { $pull: { field: <query> } } );
             
             
      1. { $pull : { field : _value } }
              
              
      1. //插入一条文档
      2. db.profiles.insert({ votes: [ 3, 5, 6, 7, 7, 8 ] });
      3. //移除数组中所有元素7
      4. db.profiles.update( { votes: 3 }, { $pull: { votes: 7 } } );
      5. //移除数组中所有大于6的元素
      6. db.profiles.update( { votes: 3 }, { $pull: { votes: { $gt: 6 } } } );
      7. //Result
      8. { votes: [ 3, 5, 6, 8 ] }
      9. { votes: [ 3, 5, 6 ] }

  • $addToSet
    • 增加一个值到数组内,而且只有当这个值不在数组内才增加。

  • $pop
    • 删除数组的第一个或最后一个元素
             
             
      1. { $pop : { field : 1 } }


  • $rename
    • 修改字段名称
             
             
      1. $rename操作符可以重命名字段名称,新的字段名称不能和文档中现有的字段名相同。
             
             
      1. 如果文档中存在AB字段,将B字段重命名为A,$rename会将A字段和值移除掉,然后将B字段名改为A.
             
             
      1.  当重命名子文档字段名时需要使用"."操作符,格式:值为该子文档的字段名.子文档中字段名。
                   
                   
        1. $rename操作符也可以将子文档中键值移到其他子文档中。
        2.                
                         
          1. db.students.update( { _id: 1 }, { $rename: { "name.last": "contact.lname" } } )

        3. 我们将名为name的子文档中的last字段,重名为“lname”,同时将其移动到子文档contact中,
        4. contact字段不存在,数据库会新建该字段。

             
             
      1. { $rename : { old_field_name : new_field_name } }
              
              
      1. 若指定的字段在集合中不存在,$rename操作符将不会有任何影响。
      2.             
                    
        1. db.students.update( { _id: 1 }, { $rename: { 'wife': 'spouse' } } )
        2.   若指定的多个字段在集合中都不存在,$rename操作符将不会有任何影响。
        3. db.students.update( { _id: 1 }, { $rename: { 'wife': 'spouse', 'vice': 'vp', 'office': 'term' } } )
        4.   集合中不存在上面语句中指定的wifeviceoffice字段,所以上述的更新操作无任何影响。

      3. 若指定的多个字段中,有的在集合中存在,有的不存在,$rename操作符执行下列操作:
                    
                    
        1. 存在的字段按照上面的规则重命名为新的名称。
        2. 不存在的字段对数据无任何影响。



  • $bit
    • 位操作,integer类型
             
             
      1. {$bit : { field : {and : 5}}}


  • 偏移操作符
         
         
    1. > t.find() { "_id" : ObjectId("4b97e62bf1d8c7152c9ccb74"), "title" : "ABC", "comments" : [ { "by" : "joe", "votes" : 3 }, { "by" : "jane", "votes" : 7 } ] }
    2. > t.update( {'comments.by':'joe'}, {$inc:{'comments.$.votes':1}}, false, true )
    3. > t.find() { "_id" : ObjectId("4b97e62bf1d8c7152c9ccb74"), "title" : "ABC", "comments" : [ { "by" : "joe", "votes" : 4 }, { "by" : "jane", "votes" : 7 } ] }
          
          
    1. update() 方法
    2. update() 方法用于更新已存在的文档。语法格式如下:
    3.           
                
      1. db.collection.update(
      2. <query>,
      3. <update>,
      4. {
      5. upsert: <boolean>,
      6. multi: <boolean>,
      7. writeConcern: <document>
      8. }
      9. )

    4. 参数说明:
      1. query : update的查询条件,类似sql update查询内where后面的。
      2. update : update的对象和一些更新的操作符(如$,$inc...)等,也可以理解为sql update查询内set后面的
      3. upsert : 可选,这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入。
      4. multi : 可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新。
      5. writeConcern :可选,抛出异常的级别。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

琦彦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值