zoukankan      html  css  js  c++  java
  • MongoDB索引、聚合

    用$where可以执行任意的js作为查询的一部分。 
    db.foo.find({"$where" : function(){ 
             for(var current in this){ 
                for(var other in this){ 
                   if(current != other && this[current] == this[other]){ 
                      return true; 
                   }     
                } 
             } 
          } 
          return false; 
    }) 

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

    $where的查询速度比常规查询速度慢很多,每个文档都要从从BSON转换成js对象,然后通过$where的表达式来运行。同样不能利用索引。 

    数据库使用游标来返回find的执行结果。客户端对游标的实现通常能够对最终结果进行有效的控制。若要在shell中创建一个游标,首先要对集合填充一些文档,然后对其执行查询,并将结果分配给一个局部变量。 

    for(i=0 ; i<100 ; i++){ 
        db.c.insert({x : i}); 

    var cursor = db.collection.find(); 

    while(cursor.hasNext()){     //hasNext()检查是否有后续结果存在 
          obj = cursor.next();   //next()获取它的值 


    var cursor = db.people.find(); 
    cursor.forEach(function(x){ 
        print(x.name); 
    }) 

    最常用的查询选项就是限制返回结果的数量,忽略一定数量的结果并排序。所有这些选项一定要在被派发到服务器之前添加。(limit skip sort) 
    db.c.find().limit(3)  //限制结果数量 
    db.b.find().skip(3)   //略过前3个文档 
    sort用一个对象作为参数:一组键/值对,键对应文档的键名,值代表排序的方向。排序方向可以是升序(1)或降序(-1)。若指定了多个键,则按照多个键的顺序逐个排序。 
    db.stock.find({"deav" : "mp3"}).limit(50).sort({"price" : -1}) 

    MongoDB处理不同类型的数据是有一个顺序的。有时候一个键值往往是多种类型的。其排序顺序是预先定义好的。从小到大的顺序是:最小值、null、数字(整型、长整型、双精度)、字符串、对象/文档、数组、二进制数据、对象ID、布尔型、日期型、时间戳、正则表达式、最大值。 

    避免使用skip略过大量结果,这样skip会变得很慢。通常可以向文档本身内置查询条件,来避免过的的skip。 
    分页(不使用skip): 
    var page1 = db.foo.find().sort({"date" : -1}).limit(100); 
    var latest = null; 
    while(page1.hasNext()){ 
        latest = page1.next(); 
        display(latest); 

    var page2 = db.foo.find({"date" : {"$get" : latest.date}}); 
    page2.sort({"date" : -1}).limit(100); 

    查询分为包装的和普通的两类。绝大多数驱动程序有些辅助措施向查询添加各种选项: 
    $maxsan : integer    制定查询最多扫描的文档数量 
    $min : document      查询的开始条件 
    $max : max           查询的结束条件 
    $hint : document     指定服务器使用哪个索引进行查询 
    $explain : boolean   获取查询执行的细节,而并非真正执行查询 
    $snapshot : boolean  确保查询的结果是在查询执行那一刻的一致快照 

    在服务器端,游标消耗内存和其他资源。游标遍历尽了结果以后,或者客户端发来消息要求终止,数据库将会释放这些资源。释放的资源可以被数据库换做他用,所以要尽量保证尽快释放游标。还有一些情况在作用域内了,驱动会向服务器发送专门的消息,让其销毁游标。最后,即便用户也没有迭代完所有结果,并且游标也还在作用域中,10分钟不用,数据库游标也会自动销毁。 

    创建索引要使用ensureIndex()方法 
    db.people.ensureIndex({"username" : 1}) 
    MongoDB的查询优化器会重排查询项的顺序,以便利用索引。 
    创建索引的缺点就是每次插入、更新和删除时都会产生额外的开销。这是因为数据库不但需要执行这些操做,还要将这些操作在索引中标记。因此,要尽可能少创建索引。每个集合默认的最大索引个数为64个。要是查询返回一半以上的结果,用表扫描会比索引高效一些。 

    集合中的每个索引都有一字符串类型的名字,来唯一标识索引。服务器通过这个名字来删除或者操作索引。索引名有字符个数的限制,所以特别复杂的索引在创建时一定要使用自定义的名字。 
    db.foo.ensureIndex({"a" : 1, "b" : 1, "c" : 1,..., "z" : 1},{"name" : "alphabet"}) 

    唯一索引可以确保集合的每一个文档的指定键都有唯一值。 
    db.people.ensureIndex({"username" : 1},{"unique" : true}) 

    当为已有的集合创建索引,这是会造成数据重复,dropDups选项可以保留发现的第一个文档,而删除接下来的有重复值的文档。 
    db.people.ensureIndex({"username" : 1},{"unique" : true, "dropDups" : true}) 

    创建复合唯一索引的时候,单个键的值可以相同,只要组合起来的值不同即可。 

    explain会帮助你获得查询方面诸多有用的信息。只要对游标调用该方法,就可以得到查询细节。explain会返回一个文档,而不是游标本身。explain会返回查询使用的索引情况,耗时及扫描文档数的统计信息。 
    db.foo.find().explain() 
    //output 

       "cursor" : "BasicCursor",  //没有使用索引 
       "indexBounds" : [ ],     
       "nscanned" : 64,           //数据库查找了多少个文档 
       "nscannedObjects" : 64, 
       "n" : 64,                  //返回文档的数量 
       "millis" : 0,              //数据库执行查询的时间 
       "allPlans" : [ 
          { 
               "cursor" : "BasicCurosr", 
               "indexBounds" : [ ] 
          } 
       ] 


    //假设查询一个基于"age"键的索引。 
    db.c.find({age : {$gt : 20, $lt : 30 }}).explain() 
    //output 

        "cursor" : "BtreeCursor age_1" 
        "indexBounds" :[ 
            [ 
                { 
                   "age" : 20 
                }, 
                { 
                    "age" : 30 
                } 
            ] 
        ], 
        "nscanned" : 14, 
        "nscannedObjects" : 12, 
        "n" : 12, 
        "millis" : 1, 
        "allPlans" : [ 
            { 
               "cursor" : "BtreeCursor age_1", 
               "indexBounds" : [  
                   [ 
                       { 
                           "age" : 20 
                       }, 
                       {  
                           "age" : 30 
                       } 
                   ] 
               ] 
            } 
        ] 


    可以使用hint强制使用某个索引。 
    db.c.find({"age" : 14, "username" : /.*/}).hint({"username" : 1, "age" : 1}) 
    多数情况下,MongoDB的查询优化器非常智能,会替你选择该用那个索引。初次做某个查询时,查询优化器会同时尝试各种查询方案。最先完成的被确定使用,其他的则终止掉。查询方案被记录下来,以备日后对应相同键的查询。查询优化器定期充实其他方案,以防因添加新的数据后,之前的方案不再是最优。 

    索引的元信息存储在每个数据库的system.indexes集合中。这是一个包保留集合。不能对其插入或者删除文档。只能通过ensureIndex或DropIndexes进行。system.indexes集合包含每个索引的详细信息,同时system.namespaces集合也含有索引的名字。 

    db.people.ensureIndex({"username" : 1},{"background" : true}) 
    建立索引既耗时又费力,还需消耗很多资源。使用{"background" : true}后,可以使这个过程在后台完成,同时正常处理请求。要是不包括{"background" : true},数据库会阻塞建立索引期间的所有请求。 

    db.runCommand({"dropIndexes" : "foo", "index" : "*"})//删除所有索引 

    地理空间索引:找到离当前位置最近的N个场所。MongoDB为坐标平面查询提供了专门的索引。 
    db.map.ensureIndex({"gps" : "2d"})gps的键值必须是某种形式的一对值:一个包含两个元素的数组或包含两个键的内嵌文档。 
    默认的情况下,地理空间索引假设值的范围是-180~180。要想用其他值,可以通过ensureIndex的选项指定最大最小值。 
    db.star.trek.ensureIndex({"light-years" : "2d"},{"min" : -1000, "max" : 1000}) 

    地理空间查询以两种方式进行,即普通查询(find)或使用数据库命令。 
    db.map.find({"gps" : [40, -73] })  //默认返回100个文档 
    db.runCommand({goNear : "map", near : [40, -70], num : 10}) 
    goNear还会返回每个文档到查询点的距离。 

    MongoDB可以找到指定形状内的文档。 
    $box  矩形(第一个参数指定了左下角的坐标、第二个指上角的坐标) 
    db.map.find({"gaps" : {"$within" : {"$box" : [10, 20], [15, 30] }}}) 
    $center  圆形内部所有的点(第一个参数指定圆心坐标、第二个参数指定半径) 
    db.map.find({"gps" : {"$within" : {"$center" : [12, 25], 5}}}) 

    count:返回集合中的文档数量 
    db.foo.count() 
    db.foo.count({"c" : 1}) 

    distinct:找出给定键的所有不同的值。使用时必须指定集合和键。 
    db.runCommand({"distinct" : "people", "key" : "age"}) 

    group:先选定分组所依据的键,而后MongoDB就会将集合依据所选定键值的不同分成若干组,然后可以通过聚合每一组内的文档,产生一个结果文档。 

       {"time" : "10/3/2010 05:00", "price" : 4.10}, 
       {"time" : "10/4/2010 04:00", "price" : 4.82}, 
       {"time" : "10/5/2010 11:00", "price" : 4.02} 

    db.runCommand({"group" : { 
         "ns" : "stocks",      //指定要进行分组的集合 
         "key" : "day",        //指定文档分组依据的键 
         "initial" : {"time" : 0},    //每一组reduce函数调用的初始时间,会作为初始文档传递给后续过程 
         "$reduce" : function(doc,prev){ //每个文档都对应一次这个调用。系统会传递两个参数:当前文档和累加器文档 
            if(doc.time > prev.time){ 
               prev.price = doc.price; 
               prev.time = doc.time; 
            } 
         }, 
          "conditon" : {"day" : {"$gt" : "2010/10/2"}} 
       } 
    }) 

    完成器(finalizer)用以精简从数据库传到用户的数据。 
    db.runCommand({"group" : { 
         "ns" : "posts", 
         "key" : {"tags" : true}, 
         "initial" : {"tags" : []}, 
         "$reduce" : function(doc, prev){ 
           for(i in doc.tags){ 
              if(doc.tags[i] in prev.tags){ 
                   prev.tags[doc.tags[i]]++; 
              }else{ 
                   prev.tags[doc.tags[i]] = 1; 
              } 
           }, 
           "finalize" : function(prev){ 
              for(i in prev.tags){ 
                  if(prev.tags[i] > mostPopular){ 
                       prev.tag = i ; 
                       mostPopular = prev.tag[i]; 
                  } 
              } 
           delete prev.tags 
           }    
         } 
       } 
    }) 

    定义分组函数要用$keyf键。 
    db.posts.group({ 
         "ns" : "posts", 
         "$keyf" : function(x) {return x.category.toLowerCase();}, 
         "initializer" : ...         
    }) 

    MapReduce是一个可以轻松并行化到多个服务器的聚合方法。它会拆分问题,再将各个部分发送到不同的机器上,让每台机器都完成一部分。当所有及其都完成的时候,再把结果汇集起来形成最终完整的结果。MapReduce需要几个步骤:最开始是映射,将操作映射到集合中的每个文档。然后就是中间环节,称为洗牌(shuffle),按照键分组,并将产生的键值组成列表放到对应的键中。化简(reduce)则把列表中的值化简成一个单值。这个值被返回,然后接着洗牌,直到每个键的列表只有一个值为止。 

    map = function(){ 
        for(var key in this){ 
             emit(key, {count : 1}); 
        } 
    }; 

    reuduce = function(key,emits){ 
        total = 0; 
        for(var i in emits){ 
            total =+ emits[i].count; 
        } 
        return {"count" : total}; 


    mr = db.runCommand({"mapreduce" : "foo", "map" : map, "reduce" : reduce}) 
    //output 

        "result" : "tmp.mr.mapreduce_165_1", //存放mapReduce结果的集合名,是一个临时集合,MapReduce的连接关闭后自动被删除 
        "timeMills" : 12,    //操作花费的时间 
        "counts" : {    
           "input" : 6      //发送到map函数的文档个数 
            "emit" : 14      //在map函数中emit被调用的次数 
            "output" : 5     //结果集合中创建的文档数量 
        } 
        "ok" : true 

    db[mr.result].find()  //对结果集合进行查询 

    map = function(){ 
       for(var i in this.tag){ 
          var recency = 1/(new Date() - this.date); 
          var score = recency * this.score; 
        
          emit(this.tag[i], {"urls" : [this.url], "score" : score}); 
       } 
    }; 
    reuduce = function(key, emits){ 
       var total = {urls : [], score : 0} 
       for (var i in emits){ 
          emits[i].urls.forEach(function(url){ 
             total.urls.push(url); 
          } 
          total.score += emits[i].score; 
       } 
       return total; 
    }; 

    MapReduce的其它可选键: 
    finalize:函数   将reduce的结果发送给这个值,这是处理过程的最后一步 
    keeptemp:布尔   连接关闭临时结果集合是否保存 
    output:字符串   结果集合的名字。设定该项则隐含着keeptemp : true 
    query:文档      会在发往map函数前,先用指定条件过滤文档 
    sort:文档       在发往map前先给文档排序 
    limit:整数      发往map函数的文档数量的上限 
    scope:文档      js代码中要用到的变量 
    verbose:布尔   是否产生更加详尽的服务器日志 

    获得MongoDB所有命令的最新列表有2种方式:在shell这运行db.listCommands();在浏览管理员接口http://localhost:28017/_commands
    MongoDB常用命令: 
    buildInfo:管理员专用命令,返回MongoDB服务的版本号和主机的操作系统 
    {"buildInfo" : 1} 
    collStats:返回指定集合的统计信息,包括数据大小、已分配的存储空间和索引大小 
    {"collStats" : collection} 
    distinct:列出指定集合中满足查询条件的文档的指定键的所有不同值 
    {"distinct" : collection, "key" :key, "query" : query} 
    drop:删除集合所有数据 
    {"drop" : collection} 
    dropDatabase:删除当前数据库的所有数据 
    {"dropDatabase" : 1} 
    dropIndexes:删除集合里面名称为name的索引,若名称为*,则删除所有索引 
    {"dropIndexes" : collection, "index" : name } 
    getLastError:查看对本集合执行的最后一次操作的错误信息或其他状态信息,在n太服务器复制集合的最后操作之前,这个命令会堵塞 
    {"getLastError" : 1[, "w" : w[, "wtimeout" : timeout]]} 
    isMaster:检查本服务器是主服务器还是从服务器 
    {"isMaster" : 1} 
    ListCommands:返回所有可以在服务器上运行的命令及相关信息 
    {"listCommands" : 1} 
    listDatabase:管理专用命令,列出服务器上所有的数据库 
    {"listDatabase" : 1} 
    ping:检查服务器链接是否正常,即便服务器上锁,也会立刻返回 
    {"ping" : 1 } 
    renameCollection:将集合a重命名为b,其中a和b都必须是完整的集合命名空间。 
    {"renameCollection" : a, "to" : b} 
    repairDatabase:修复并压缩当前数据库,这个操作可能非常耗时 
    {"repairDatabase" : 1} 
    serverStatus:返回这台服务器的管理统计信息 
    {"serverStatus" : 1} 

    MongoDB支持固定集合,要事先创建并固定大小。当空间不足时,最早的文档就会被删除。在默认的情况下,固定集合没有索引。 
    创建固定集合: 
    db.createCollection("my_collection", {capped : true, size : 100000, max = 100}); 
    通过转换已有的普通集合的方式来创建固定集合: 
    db.runCommand({convertToCapped : "test", size : 10000}) 
    固定集合有种特殊的排序方式,叫做自然排序。自然排序就是文档在磁盘上的顺序。 

    尾部游标是一种特殊的持久游标,这类游标不会在没有结果后销毁。游标收到tail-f命令的启发,会尽可能持续地获取结果输出。因为这类游标在没有结果后不销毁,所以一旦有新文档添加到集合里面就会被取回并输出。尾部游标只能用在固定集合上。

  • 相关阅读:
    NET5 ORM 六大新功能
    牛逼程序员必须要掌握金字塔思维
    实体类转Json的2种方法
    怎么使用jquery阻止页面的离开或卸载
    GitHub的用法:到GitHub上部署项目
    搭建个人服务器
    远程服务器上部署本地项目
    java.nio.ByteBuffer中flip,rewind,clear方法的区别
    eclipse Run On Server 异常:could not load the Tomcat Server configuration at Servers omcat V5.0 Sertomcat
    throw与throws的区别
  • 原文地址:https://www.cnblogs.com/forerver-elf/p/4724156.html
Copyright © 2011-2022 走看看