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