设为首页 收藏本站
查看: 911|回复: 0

[经验分享] MongoDB权威指南(2)-Creating,Updating and Deleting Documents

[复制链接]

尚未签到

发表于 2015-7-7 10:41:42 | 显示全部楼层 |阅读模式
  1.插入和保存document
  如前所述,向collection插入document使用insert方法


> db.foo.insert({"bar" : "baz"})  如果document里边没有"_id"键,"_id"会被自动创建
  批量插入
  批量插入是一种更高效的方法,传递给database一个document的数组,可以一次插入多个document。单个插入的时候,向database传送一个document,前边会附加一个头部,告诉database在某个collection执行一次插入操作。批量插入只产生一个TCP请求,意味着不用处理很多请求,同时也省掉了处理头部的时间。批量插入只能插入到一个collection里边去。
  批量插入只能用于应用程序接口,shell不支持(至少到目前还不支持)。
  另外,如果想导入数据(比如说从mysql),不要使用批量插入,使用命令行工具如mongoimport。
  2.删除document


> db.users.remove()  这个命令会删除users里边的所有document。
  remove函数可以有一个查询用document做参数,以删除符合条件的document。


> db.mailing.list.remove({"opt-out" : true})  这个命令删除所有"opt-out"为true的document。
  删除文档通常是一个非常快的操作,如果想清除整个collection,还有一种更快的方法,使用drop函数然后重建索引。
  3.更新document
  udpate方法可以携带两个参数:

  • 查询用document, 用于定位哪些document将会被更新
  • 修饰符document,用于描述如何修改找到的document
  更新是原子性操作,先到达服务器的将会被先执行,后到达的会被后执行,所以,后边的会覆盖前边的修改。
  document替换
  使用一个新的document来替换匹配的,上一篇文章里用的其实就是document替换,如


db.users.update({"name" : "joe"}, joe);  document替换时一个常见的错误是当有多个匹配的document时候可能会导致duplicate key错误。举个例子,
  假设我们有好几个名字都叫joe的document,


> db.people.find()
{"_id" : ObjectId("4b2b9f67a1f631733d917a7b"), "name" : "joe", "age" : 65},
{"_id" : ObjectId("4b2b9f67a1f631733d917a7c"), "name" : "joe", "age" : 20},
{"_id" : ObjectId("4b2b9f67a1f631733d917a7d"), "name" : "joe", "age" : 49},  现在,2号joe(20岁那个)生日到了,我们要给他的年龄加1,


> joe = db.people.findOne({"name" : "joe", "age" : 20});
{
  "_id" : ObjectId("4b2b9f67a1f631733d917a7c"),
  "name" : "joe",
  "age" : 20
}
> joe.age++;
> db.people.update({"name" : "joe"}, joe);
E11001 duplicate key on update  Oh,出错了,怎么回事?数据库查找name为joe的document,找到的第一个是65岁那个,然后试图替换这个document,然而
  数据库里边已经有一个"_id"为"4b2b9f67a1f631733d917a7c" 的记录了,”_id"是不可重复的,所以就有个这个错误。
  所以执行document替换的时候要小心,确认你要替换的是唯一一个符合条件的。
  使用修饰符
  通常情况下我们只想更新document的一部分,我们可以使用更新修饰符来做到这一点。
  假设我们有一个记录网站访问信息的一个collection,里边的document像这个样子


{
  "_id" : ObjectId("4b253b067525f35f94b60a31"),
  "url" : "www.example.com",
  "pageviews" : 52
}  pageviews是站点的访问次数,那么我想给它增加1的时候就可以这样子做


> db.analytics.update({"url" : "www.example.com"},
... {"$inc" : {"pageviews" : 1}})  "$inc"就是个更新修饰符,使用更新修饰符的时候,不能更新"_id"键的值。
  下边我们看看常用的更新修饰符

  • $set
    $set修饰符设定指定的key的值,如果key不存在就创建一个
    假设我们有下边一个用户档案


    > db.users.findOne()
    {
      "_id" : ObjectId("4b253b067525f35f94b60a31"),
      "name" : "joe",
      "age" : 30,
      "sex" : "male",
      "location" : "Wisconsin"
    }现在我们想给用户加一个项目,他喜爱的书籍,我们就可以用$set修饰符来做


    > db.users.update({"_id" : ObjectId("4b253b067525f35f94b60a31")},
    ... {"$set" : {"favorite book" : "war and peace"}})看看结果


    > db.users.findOne()
    {
      "_id" : ObjectId("4b253b067525f35f94b60a31"),
      "name" : "joe",
      "age" : 30,
      "sex" : "male",
      "location" : "Wisconsin",
      "favorite book" : "war and peace"
    }如果用户不喜欢这本书了,喜欢另为一本,我们可以再次用$set来修改


    > db.users.update({"name" : "joe"},
    ... {"$set" : {"favorite book" : "green eggs and ham"}})$set不仅可以改变值,还可以改变数据类型,如果用户喜欢的不是一本,而是很多书


    > db.users.update({"name" : "joe"},
    ... {"$set" : {"favorite book" :
    ... ["cat's cradle", "foundation trilogy", "ender's game"]}})如果用户现在又觉得,他其实并不喜欢读书,我们可以用$unset来删除key


    > db.users.update({"name" : "joe"},
    ... {"$unset" : {"favorite book" : 1}})
  • $inc
    $inc修饰符只能用于数字,增加一个指定的数量
  • 数组修饰符$push
    $push向数组尾部追加一个元素,如果数组不存在则创建一个数组
    例如,我想给一篇博客文章添加评论,而评论这个key还不存在


    > db.blog.posts.findOne()
    {
      "_id" : ObjectId("4b2d75476cc613d5ee930164"),
      "title" : "A blog post",
      "content" : "..."
    }
    > db.blog.posts.update({"title" : "A blog post"}, {$push : {"comments" :
    ... {"name" : "joe", "email" : "joe@example.com", "content" : "nice post."}}})
    > db.blog.posts.findOne()
    {
      "_id" : ObjectId("4b2d75476cc613d5ee930164"),
      "title" : "A blog post",
      "content" : "...",
      "comments" : [
      {
        "name" : "joe",
        "email" : "joe@example.com",
        "content" : "nice post."
      }
      ]
    }如果想添加另外一篇评论,就再次使用$push


    > db.blog.posts.update({"title" : "A blog post"}, {$push : {"comments" :
    ... {"name" : "bob", "email" : "bob@example.com", "content" : "good post."}}})
    > db.blog.posts.findOne()
    {
      "_id" : ObjectId("4b2d75476cc613d5ee930164"),
      "title" : "A blog post",
      "content" : "...",
      "comments" : [
      {
        "name" : "joe",
        "email" : "joe@example.com",
        "content" : "nice post."
      },
      {
        "name" : "bob",
        "email" : "bob@example.com",
        "content" : "good post."
      }
      ]
    }如果想向数组里追加一个数组里没有的元素,我们可以在查询document里使用$ne(not equal,第四章查询里会有这种逻辑修饰符的说明),如:


    > db.papers.update({"authors cited" : {"$ne" : "Richie"}},
    ... {$push : {"authors cited" : "Richie"}})数组修饰符$addToSet可以达到相同的效果,假设我们有个用户document,用户有好几个email


    > db.users.findOne({"_id" : ObjectId("4b2d75476cc613d5ee930164")})
    {
      "_id" : ObjectId("4b2d75476cc613d5ee930164"),
      "username" : "joe",
      "emails" : [
        "joe@example.com",
        "joe@gmail.com",
        "joe@yahoo.com"
      ]
    }我们可以使用$addToSet防止插入重复的值


    > db.users.update({"_id" : ObjectId("4b2d75476cc613d5ee930164")},
    ... {"$addToSet" : {"emails" : "joe@gmail.com"}})
    > db.users.findOne({"_id" : ObjectId("4b2d75476cc613d5ee930164")})
    {
      "_id" : ObjectId("4b2d75476cc613d5ee930164"),
      "username" : "joe",
      "emails" : [
        "joe@example.com",
        "joe@gmail.com",
        "joe@yahoo.com",
      ]
    }
    > db.users.update({"_id" : ObjectId("4b2d75476cc613d5ee930164")},
    ... {"$addToSet" : {"emails" : "joe@hotmail.com"}})
    > db.users.findOne({"_id" : ObjectId("4b2d75476cc613d5ee930164")})
    {
      "_id" : ObjectId("4b2d75476cc613d5ee930164"),
      "username" : "joe",
      "emails" : [
        "joe@example.com",
        "joe@gmail.com",
        "joe@yahoo.com",
        "joe@hotmail.com"
      ]
    }$addToSet还可以和$each联合使用,一次追加多个不重复的值,这个是$ne/$push组合做不到的


    > db.users.update({"_id" : ObjectId("4b2d75476cc613d5ee930164")}, {"$addToSet" :
    ... {"emails" : {"$each" : ["joe@php.net", "joe@example.com", "joe@python.org"]}}})
    > db.users.findOne({"_id" : ObjectId("4b2d75476cc613d5ee930164")})
    {
      "_id" : ObjectId("4b2d75476cc613d5ee930164"),
      "username" : "joe",
      "emails" : [
        "joe@example.com",
        "joe@gmail.com",
        "joe@yahoo.com",
        "joe@hotmail.com"
        "joe@php.net"
        "joe@python.org"
      ]
    }
  • 数组修饰符$pop
    $pop修饰符从数组的两端移除一个元素,{$pop : {key : 1}}从数组末端删除一个元素,{$pop :{key : -1}}从数组起始端删除一个元素
  • 数组修饰符$pull
    $pull从数组里移除符合条件的元素
    比如,我们有一个待做事项列表


    > db.lists.insert({"todo" : ["dishes", "laundry", "dry cleaning"]})我们想把laundry删掉


    > db.lists.update({}, {"$pull" : {"todo" : "laundry"}})这样数组里就剩两项了


    > db.lists.find()
    {
      "_id" : ObjectId("4b2d75476cc613d5ee930164"),
      "todo" : [
        "dishes",
        "dry cleaning"
      ]
    }$pull会删除所有的匹配元素
  • 按照位置操作数组的值
    有两种方式:一是按照位置,二是使用位置操作符($符号)
    我们先看第一种用法,位置是从0开始索引的,我们可以使用这个索引就好像它是数组的一个属性一样
    假设我们有一篇博客,带有一些评论


    > db.blog.posts.findOne()
    {
      "_id" : ObjectId("4b329a216cc613d5ee930192"),
      "content" : "...",
      "comments" : [
      {
        "comment" : "good post",
        "author" : "John",
        "votes" : 0
      },
      {
        "comment" : "i thought it was too short",
        "author" : "Claire",
        "votes" : 3
      },
      {
        "comment" : "free watches",
        "author" : "Alice",
        "votes" : -1
      }
      ]
    }我想给第一篇评论的投票数加1,我们就可以这样做


    > db.blog.update({"post" : post_id},
    ... {"$inc" : {"comments.0.votes" : 1}})实际上很多时候我们根本不知道这个索引是多少,我们只知道有这么个匹配的document在,我们可以使用第二种用法,使用位置操作符$,
    $就代表了匹配的元素的索引,如果我们想把评论里叫John的那个改成Jim,就可以这样子做


    db.blog.update({"comments.author" : "John"},
    ... {"$set" : {"comments.$.author" : "Jim"}})
  4.Upsert
  这估计是作者自己造的单词,指如果存在匹配的document就更新,如果不存在匹配就插入。
  将update函数的第三个参数设为true即可,如:


db.analytics.update({"url" : "/blog"}, {"$inc" : {"visits" : 1}}, true)  
  shell的save函数也可以达到同样的目的,如果存在就更新,如果不存在就插入。
  save函数使用一个document做参数,如果document有"_id"键就更新,如果没有就插入。


> var x = db.foo.findOne()
> x.num = 42
42
> db.foo.save(x)  5.更新多个document
  缺省情况下,update函数只更新匹配的第一条记录,余下的不做改变,要想更新所有的匹配记录,将update函数的第4个参数设为true


> db.users.update({birthday : "10/13/1978"},
... {$set : {gift : "Happy Birthday!"}}, false, true)  6.返回被更新的document
  findAndModify命令的调用比普通的update要慢一些,因为它要等待服务器的响应。
  findAndModify命令适合处理队列,或者其他的原子性的get-and-set式的操作。
  假设我们有一个处理流程的collection,需要按一定的顺序执行,一个document代表了一个处理流程,如下


{
  "_id" : ObjectId(),
  "status" : state,
  "priority" : N
}  status是个字符串,可能的值是"Ready","Running","Done".我们需要找到Ready状态优先级最高的处理流程,处理完成后把状态设为Done。
  我们查询Ready状态的所有流程,按优先级排序,把最高的那个标记为Running,然后执行处理流程,结束后把状态设为Done。


ps = db.processes.find({"status" : "READY").sort({"priority" : -1}).limit(1).next()
db.processes.update({"_id" : ps._id}, {"$set" : {"status" : "RUNNING"}})
do_something(ps);
db.processes.update({"_id" : ps._id}, {"$set" : {"status" : "DONE"}})  这个算法并不好,会产生资源竞争。假设我们有两个线程来处理,一个线程(线程A)获取了document,另一个线程(线程B)可能在A将状态设置为Running之前获取同一个document,然后两个线程会执行同一个处理流程。我们可以将检查status作为update的一部分来避免这个问题,不过会变得复杂:


var cursor = db.processes.find({"status" : "READY"}).sort({"priority" : -1}).limit(1);
while ((ps = cursor.next()) != null) {
  ps.update({"_id" : ps._id, "status" : "READY"},
        {"$set" : {"status" : "RUNNING"}});
  var lastOp = db.runCommand({getlasterror : 1});
  if (lastOp.n == 1) {
    do_something(ps);
    db.processes.update({"_id" : ps._id}, {"$set" : {"status" : "DONE"}})
    break;
  }
  cursor = db.processes.find({"status" : "READY"}).sort({"priority" : -1}).limit(1);
}  这样有另外一个问题,依赖于运行时,一个线程可能处理完所有的工作然后结束,而另一个线程无用的跟在后边。线程A总是能获取处理流程,线程B试图获取同一个处理流程,然后失败,然后看着A完成所有的工作。这种情况就非常适合使用findAndModify命令,findAndModify命令在同一个操作里返回项目并更新它。


> ps = db.runCommand({"findAndModify" : "processes",
... "query" : {"status" : "READY"},
... "sort" : {"priority" : -1},
... "update" : {"$set" : {"status" : "RUNNING"}})
{
  "ok" : 1,
  "value" : {
    "_id" : ObjectId("4b3e7a18005cab32be6291f7"),
    "priority" : 1,
    "status" : "READY"
  }
}  Note:返回的document中的状态仍然是Ready,在修饰符生效之前,document已经返回了。
  执行find查看就可以看到status被设置为了Running


> db.processes.findOne({"_id" : ps.value._id})
{
  "_id" : ObjectId("4b3e7a18005cab32be6291f7"),
  "priority" : 1,
  "status" : "RUNNING"
}  所以我们的程序应该是这个样子:


> ps = db.runCommand({"findAndModify" : "processes",
... "query" : {"status" : "READY"},
... "sort" : {"priority" : -1},
... "update" : {"$set" : {"status" : "RUNNING"}}).value
> do_something(ps)
> db.process.update({"_id" : ps._id}, {"$set" : {"status" : "DONE"}})  findAndModify命令里含有一个"update"键或"remove"键,remove表示匹配的document会被从collection里删除。
  findAndModify命令里各个key的值意义如下

  • findAndModify: 字符串,collection的名字


  • query:查询document,检索document的条件


  • sort:按照什么排序


  • update:修饰符document,如何更新匹配的document


  • remove:布尔值,指示是否删除document


  • new:布尔值,指示返回的document是更新前的还是更新后的,缺省为更新前的。
  7.密西西比河此岸的最快书写(The Fastest Write This Side of Mississippi)
  ^-^,好吧,我确实不知道这个题目什么意思,也没找到This side of Mississippi有何典故,求高人指点。
  本章节所关注的三个操作(insert,update,remove)看起来都是瞬发的,因为它们不会等待服务器的响应。
  这并不是异步,应当被看作是"fire-and-forget"型的函数:客户端向服务器发送了document然后就继续自己的事情,客户端从不会收到一个响应诸如“ok,我收到你的消息啦”或者“不ok,你得给我重新发送一次”之类的东西。
  安全操作
  这些操作的安全版本就是在执行操作之后立刻调用getLastError命令。驱动会等待服务器响应并做相应的处理,通常是抛出一个异常,开发人员可以捕获然后处理。
  操作成功之后,getLastError也会返回一些信息,比如update或remove,信息里包含了受影响的document数。
  请求和连接
  数据库为每个到mongoDB的连接建立一个请求队列,客户端发出一个请求,就会被放到队列的尾部。
  注意是一个连接一个队列,如果我们打开两个shell,那么我们有了两个连接,如果我们在一个shell里执行插入,然后在另一个shell里执行查询,有可能得不到刚才插入的document。在同一个shell里执行插入和查询不会有问题,插入的document会被返回。这种情况在使用Ruby,Python 和Jave驱动时尤其值得注意,因为它们都是用连接池,出于性能上的考虑,这些驱动打开多个连接,然后将请求分配给它们。不过它们本身都有自己的机制,保证一系列的请求会使用一个连接来处理。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-84049-1-1.html 上篇帖子: MongoDB基础篇 下篇帖子: 开篇文章-NoSQL之MongoDB基本操作
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表