• MongoDB 查询下


    1.查询内嵌文档

    有两种方法查询内嵌文档:查询整个文档,或者只针对其键值对进行查询

    查询整个内嵌文档与普通查询相同

    如:

    db.users.insert(
      {
        "name":
        {
          "first":"refactor",
          "last":"refactor2"
        },
        "age":24
      }
    )

    查找姓名为refactor的文档

    db.users.find({"name":{"first":"refactor","last":"refactor2"}})

    但是 

    db.users.find({"name":{"first":"refactor"}})这样不能查询出文档.

    所以最好通过内嵌文档的特定的键值来进行查找.这样即便数据模式改变,也不会导致查询失效.

    如:

    db.users.find({"name.first":"refactor","name.last":"refactor2"})
    db.users.find({"name.first":"refactor"})//这个也可以查出文档.

    查询文档可以包含点,来表示"深入内嵌文档内部"的意思.点表示法也是待插入的文档不能包含"."的原因.

    当文档结构变得更加复杂后,内嵌文档的匹配需要技巧

    如:

    db.blog.insert(
      {
        "title":"refactor's blog",
        "Content":"refactor's blog test",
        "comments":
        [
          {
            "author":"refactor",
            "score":3,
            "commnent":"nice"
          },
          {
            "author":"refactor2",
            "score":6,
            "commnent":"good"
          }
        ]
      }
    )

    要查询键author为refactor,score大于等于5的评论

    不能使用

    db.blog.find(
      {"comments":{"author":"refactor","score":{"$gte":5}}}
    )来查询.

    内嵌文档匹配要求整个内嵌文档完全匹配,而这不会匹配"comments"键.使用

    db.blog.find(
      {"comments.author":"refactor","comments.score":{"$gte":5}}
    )也不会达到目的,因为符合author条件的评论和符合score条件的评论可能不是同一条评论.也就是说,

    会返回刚才显示的那个文档.因为"author":"refactor"在第一条评论中匹配了,"score":6在第二条评论中匹配了.

     要正确指定一组条件,而不用指定每个键,要使用"$elemMatch".这种模糊的命名条件能用来部分指定匹配数组中

    的单个内嵌文档的限定条件.正确写法应该是:

    db.blog.find(
      {
        "comments":
        {
          "$elemMatch":
          {
            "author":"refactor",
            "score":{"$gte":5}
          }
        }
      }
    )

    "$elemMatch"将限定条件进行分组,仅当需要对一个内嵌文档的多个键操作时才会用到.

    2."$where"查询

    "$where"可以执行任意javascript作为查询的一部分.这使得查询能做(几乎)任何事情.

    最典型的应用就是比较文档中的两个键的值是否相等.

    如:

    db.blog.insert({"title":"refactor","content":"refactor"})
    db.blog.insert({"title":"refactor1","content":"refactor content"})

    第一个文档title键和content键的值相同.要返回该文档.

    db.blog.find(
      {
        "$where":function()
              {
                for(var current in this)
                {
                  for(var other in this)
                  {
                    if(current!=other&&this[current]==this[other])
                    {
                      return true;
                    }
                  }
                }
                return false;
              }
      }
    )
    如果函数返回true,文档就作为结果的一部分被返回.

    上面是用一个函数,也可以用一个字符串指定"$where"查询.下面两种方式是等价的:

    db.blog.find({"$where":"this.x+this.y==10"})
    db.blog.find({"$where":"function(){return this.x+this.y==10;}"})

    不是非常必要时,应避免"$where"查询.它在速度上要比常规查询慢的多.每个文档都要从BSON转换成

    javascript对象,然后通过"$where"的表达式来运行.它还不能利用索引.

    将常规查询作为前置过滤,与"$where"组合使用才能不牺牲性能,如果有可能的话,用索引根据非"$where"

    子句进行过滤,"$where"只用于对结果进行调优.

    3.游标

    数据库使用游标来返回find的执行结果.客户端对游标的实现通常能够对最终结果进行有效的控制.可以限制

    结果的数量,略过部分结果,根据任意方向任意键的组合对结果进行各种排序,或者是执行其他一些强大的操作.

    要想在shell中创建一个游标,首先要对集合填充一些文档,然后对齐执行查询,并将结果分配给一个局部变量.

    如:

    for(i=0;i<100;i++){db.refactor.insert({"refactorValue":i});}

    var cursor=db.refactor.find();
    cursor.forEach(
      function(x)
      {
        print(x.refactorValue);
      }
    )

    这样做的好处是一次可以查看一条结果.如果将结果放在全局变量或者就没有放在变量中,MongoDB shell

    会自动迭代,自动显示最开始的若干文档.

    var cursor=db.refactor.find();当调用find的时候,shell并不立即查询数据库,而是等待真正开始要求获得结果

    的时候才发送查询,这样执行之前可以给查询附加额外的选项.几乎所有游标对象的方法都返回游标本身,这样

    可以按照任意顺序组成方法链.下面几种方式是等价的:

    var cursor=db.refactor.find().sort({"refactorValue":1}).limit(1).skip(10);
    var cursor=db.refactor.find().limit(1).sort({"refactorValue":1}).skip(10);
    var cursor=db.refactor.find().skip(10).sort({"refactorValue":1}).limit(1);

    此时,查询还没有执行,所有这些函数都只是构造查询,执行如下操作:

    cursor.hasNext()

    这时,查询会发往服务器.shell立即获取前100个结果或者前4M数据(两者之中的较小者).等到客户端用光了第一组结果

    ,shell会再一次连接数据库,并请求结果,这个过程一直会持续到游标好近或者结果全部返回.

    4.limit,skip,sort

    最常用的查询选项是限制返回结果的数量,忽略一定数量的结果并排序,所有这些选项一定要在查询被派发到服务器之前添加

    限制结果数量,在find后使用limit函数

    如:返回5个结果:

    db.refactor.find().limit(5)

    如果匹配的结果不到5个,则返回匹配数量的结果.

    skip和limit类似.

    db.refactor.skip(5)

    略过前5个,然后返回余下的文档.如果集合里匹配的小于5个,这不返回任何文档.

    sort用一个对象作为参数:一组键值对,键对应文档的键名,值代表排序方向.

    排序方向可以是1(升序),-1(降序).如果指定了多个键,则按照键的顺序逐个排序.

    如:

    按照username升序,age降序

    db.refactor.find().sort({"username":1,age:-1}) 

    这三个方法可以组合使用来进行分页.

    如果略过过多的结果会导致性能问题,尽量避免.

    5.MongoDB的比较顺序

    MongoDB处理不同的类型的数据是有顺序的.有时候一个键的值可能是多种类型的,如:整数,布尔型,字符串或null.

    如果对这种混合类型的键进行排序,其排序顺序从小到大如下:

    最小值,null,数字,字符串,对象/文档,数组,二进制数据,对象id,布尔型,日期型,时间戳,正则表达式,最大值.

    6.避免使用skip略过大量结果

    用skip略过大量结果,基本每个数据库都会很慢.通常可以向文档本身内置查询条件,来避免过大的skip,

    或者利用上次的结果来计算下一次的查询.

    7.不用skip对结果进行分页

    最简单的分页方法:

    var page1=db.refactor.find().limit(100)
    var page2=db.refactor.find().skip(100).limit(100)
    var page3=db.refactor.find().skip(200).limit(100)

    但是,一般来说可以找到一种方法实现不用skip的分页,这取决于查询本身.

    如:要按照"date"降序显示文档

    var page1=db.refactor.find().sort({"date":-1}).limit(100)

    然后利用最后一个文档中"date"的值作为查询条件,获取下一页:

    var last=null
    while(page1.hasNext())
    {
      last=page1.next();
      display(last);
    }
    var page2=db.refactor.find({"date":{"$gt":last.date}});
    page2.sort({"date":-1}).limit(100)

    8.随机选取文档

    从集合里面随机选取一个文档的方法

    1.先计算文档总数,然后选择一个从0到文档数量之间的随机数,利用find做一次查询,略过随机数那么多

    的文档,取一个文档

    var total=db.refactor.count();
    var random=Math.floor(Math.random()*total)
    db.refactor.find().skip(random).limit(1)
    这种方法性能低,首先得计算总数,然后大量的skip也会很耗时.

    2.在插入文档时,给每个文档添加一个额外的随机键.

    db.refactor.insert({"name":"refactor","random":Math.random()})
    db.refactor.insert({"name":"refactor1","random":Math.random()})
    db.refactor.insert({"name":"refactor2","random":Math.random()})

    var random=Math.random()
    result=db.refactor.findOne({"random":{"$gte":random}})
    if(result==null)
    {
      result=db.refactor.findOne({"random":{"$lte":random}})
    }

    9.高级查询选项

    查询分为 "包装的" 和 "普通的" 的两类.

    普通的查询如:db.refactor.find({"name":"refactor"})

    包装的查询如:db.refactor.find({"name":"refactor"}).sort({"name":1})

    实际情况不是将{"name":"refactor"}最为查询直接发给数据库,而是将查询包装在一个更大的文档中.

    shell会把查询从db.refactor.find({"name":"refactor"}).sort({"name":1})转化成

    {"$query":{"name":"refactor"},"$orderby":{"name":1}}

    绝大多少驱动程序有些辅助措施向查询添加各种选项,如:

    $maxscan:integer指定查询最多扫描的文档数量

    $min:document 查询的开始条件

    $max:document 查询的结束条件

    $hint:document 指定服务器使用哪个索引进行查询

    $explain:bool 获取查询执行的细节(用到的索引,结果数量,耗时等),而并非真正的想hi行查询.

    $snapshot:bool 确保查询的结果在查询执行那一刻的一致快照.

    10.游标内幕

    看待游标有两种角度:客户端的游标以及客户端游标表示的数据库游标.

    在服务器端,游标消耗内存和其他资源.游标遍历完结果之后,或者客户端发来消息要求终止,数据库将会释放这些资源.

    释放的资源可以被数据库换做他用.所以要尽快释放游标.

    当游标完成匹配结果的迭代事,它会清除自身.

    当游标在客户端已经不在作用域内了,驱动会向服务器发送专门的消息,让其销毁.

    即便用户没有迭代完所有的结果,并且游标也在作用域内,10分钟不使用,数据库游标也会自动销毁.

    如果希望游标持续的时间长一些,多数驱动程序都实现了一个叫immortal函数,或者类似的机制,来告知数据库不要让

    游标超时.如果关闭了游标的超市时间,则一定要在迭代完结果后将其关闭,否则它会一直在数据库中消耗服务器资源.

  • 相关阅读:
    FastJson的简单使用
    一些没用过的方法的学习
    Windows系统激活
    mysql数据库运行性能检查脚本
    基于windows 的Apache HTTP Server的一次小安装
    MySQL、Oracle批量插入、更新批量inisert、update
    IDEA中右键没有“Subversion”相关目录解决方法
    关于spring boot项目启动报错问题
    我的2016
    用intellij IDEA 编写时,无编程提示问题
  • 原文地址:https://www.cnblogs.com/refactor/p/2591544.html
Copyright © 2020-2023  润新知