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命令的启发,会尽可能持续地获取结果输出。因为这类游标在没有结果后不销毁,所以一旦有新文档添加到集合里面就会被取回并输出。尾部游标只能用在固定集合上。

原文地址:https://www.cnblogs.com/forerver-elf/p/4724156.html