`
forever1121
  • 浏览: 15320 次
  • 性别: Icon_minigender_2
  • 来自: 齐齐哈尔
社区版块
存档分类
最新评论

MongoDB权威指南_学习笔记2

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

相关推荐

Global site tag (gtag.js) - Google Analytics