find()
查询结合的所有内容,如db.users.find({"age":27,"username":"joe"})
第二个参数控制返回指定的键
返回指定的键“age”:db.users.find({},{"age":1})
剔除指定的键“age”:db.users.find({},{"age":0})
限制:文档的值必须是常量,如果需要查询变量则需要修改文档,以转化为常量查询方式。
查询条件
条件:"$lt","$lte","$gt","$gte","$ne"(适应于所有数据类型) 分别对应<,<=,>,>=,!=
db.users.find({"age":{"$gte":10,"$lte":30}}) 10-30(含)岁之间的用户
OR
"$in","$nin"单一键与多个值匹配不匹配,可以与不同类型数据比较 db.raffle.find({"ticket_no":{"$in":[123,456,"789"]}})
"$or"多个条件 db.raffle.find({"$or":[{"tick_no":{"$in":[123,456,"789"]}},{"winner":true}]})
Not
"$not"是元语句,可以作用在任何条件上,特别是与正则连用时
如db.user.find({"id_num":{"$not":{"$mod":[5,1]}}}) 返回所有id除5不余1的用户
查询针对外层文档,修改针对外层文档,修改器无法使用多次,如{"$inc":{"age":1},"set":{age:40}}是错误的,但是查询是可以的
特定类型查询
null
匹配自身为null或“不存在” 键的文档
db.c.find({"y":null}) 会返回所有y=null或者不含y键的文档
如果仅仅返回自身为null的,需要配合"$exits"
db.c.find({"y":{"$in":[null],"$exits":true}}) 注意,因为没有用"$eq"的操作符,只能采用单元素的"$in"操作符替代
正则表达式
采用Perl兼容的正则表达式PCRE库来匹配正则,可用Javascript Shell检查语法,另外,可用前缀型正则表达式(如/^joey/)创建索引,所以这种类型的查询很高效
db.users.find({"name":/joey?/i})
也可匹配自身,即把正则表达式作为值存入文档也是可以匹配的(虽然几乎没人这么做)
db.user.insert({"bar":/baz/})
db.user.find({"bar":/baz/})
查询数组
数组的查询可以理解为每一个数组中的元素都是该键的值
db.food.insert({"fruit":["apple","banana","peach"]}) 查询db.food.find({"fruit":"apple"})会成功匹配该文档
"$all"多个元素匹配数组 db.food.find({"fruit":{$all:["apple","banana"]}}) 顺序无关紧要
数组的完整精确匹配 db.food.find({"fruit":["apple","banana","peach"]})是可以匹配刚插入的数据,但是db.food.find({"fruit":["apple","banana"]})却无法匹配
组下标查询 db.food.find({"fruit.2":"peach"}) 会找到数组中第三个元素与"peach"匹配的数组
"$size"查询长度为指定值的数组,但是它不能与"$gt"等查询字句组合,因此范围查询需要增加一个size字段,并且在更新的时候"$inc":{"size":1},但是该技巧不能与"$addToSet"并用
db.food.find({"fruit":{"$size":3}})
"$slice"返回数组中的一个子集合
db.blogs.findone({"criteria",{"comments":{"$slice":10}}}) 返回前10条评论
db.blogs.findone({"criteria",{"comments":{"$slice":-10}}}) 返回后10条评论
db.blogs.findone({"criteria",{"comments":{"$slice":[23,10]}}}) 返回前24-33条评论,如果不足33,则返回24之后所有的
注:除非特别声明,否则它将返回文档中所有的键,而其他键都是只返回查询的键。
如有
{ "title":"a blog list", "content":"...", "comments":[ {"name":"joe","email":joe@163.com}, {"name":"bob","email":bob@163.com}] }
db.blogs.findone({"criteria",{"comments":{"$slice":-1}}}) 返回的是":{"title":"a blog list","content":"...","comments":[{"name":"bob","email":bob@163.com}],把title,content键也都返回来了
查询内嵌文档
{ "name":{ "first":"joe" "last":"schmoe" }, "age":45 }
查询整个文档
db.people.find({"name":{"first":"joe","last":"schmoe"}}) 是可以匹配的,但是这种查询是精确匹配,而且是顺序相关的,即:如果添加了一个"middle"将不会匹配,同理,如果{"last":"schmoe","first":"joe"}})同样不会匹配
针对键值查询
db.people.find({"name.first":"joe","name.last":"schmoe"}) 在name增加更多键后仍然匹配,"."表示法表示深入文档内部的含义,所以插入文档中不能包含"."关键字
更为复杂的查询
{ "content":"...", "comments":[ { "author":"joe"; "score":3, "comment":"nice post" }, { "author":"mary"; "score":6, "comment":"terrible post" } ] }
查找joe发表的5分以上的评论
用db.blogs.find({"comments":{"author":"joe","score":{"$gt":5}}}) 是无法匹配的,因为内嵌文档要求完全精确匹配
用db.blogs.find({"comments.author":"joe","comments.score":{"$gt":5}}) 也是无法匹配的,解释为:符合author和score的评论不一定是同一条评论,即author在第一条中匹配了,joe在第二条中匹配了。正确方法如下:
"$elemMatch" db.blog.find({"comments":{"$elemMatch":{"author":"joe","score":{"$gt":5}}}})
"$where"
使用javascript作为查询的一部分,但是不推荐使用,因为其执行速度比常规查询缓慢,因为每个文档首先要从BSON转化为JS对象,还不能使用索引。
如果必须使用,请先将常规查询作为条件进行过滤。或者用非"$where"进行过滤,用"$where"进行调优
如一个文档:db.foo.insert({"apple":1,"spinach":4,"watermelon":4}) 我想找到spinach与watermelon值相同的文档,这种绝对不会有对应的"$"方法,只能自己依靠编程解决
db.foo.find("$where":function(){ for(var cur in this) { for(var other in this) { if(cur!=other&&this[cur]==this[other]) { return true; } } } })
返回true的这部分文档将会被返回
游标
控制返回结果的数量,忽略部分结果,根据任意方向任意键的组合对结果排序等
var cursor=db.collection.find()
迭代结果,可以用遍历方法
while(cursor.hasNext) { obj=cursor.next(); //do stuff }
游标类实现了迭代器接口
cursor.forEach(function(x) { print(x.name) });
调用find时,shell不去真正查库,而是在要求获得结果时才发送查询,很像c#中的延迟查询或延迟加载,因此可以加一系列的限定条件
并且,因为游标对象的方法返回的是游标本身,因此可以按照任意顺序构成方法链。如下面三个完全等价。
var cursor=db.foo.find().sort({"x":1}).limit(1).skip(10)
var cursor=db.foo.find().skip(10) .sort({"x":1}).limit(1)
var cursor=db.foo.find().limit(1).sort({"x":1}).skip(10)
但这时候查询并未执行,当发生cursor.hasNext()的时候,shell获取前100个或前4MB数据(较小者),这样next或者hasNext的时候都不用跑到服务器去请求,知道客户端用完第一组结果,shell才会再次联系数据库,请求更多结果,这个过程持续到游标耗尽或者结果全部返回。
limit,skip,sort
limit限制数量,skip略过数量,sort排序(1升序,-1降序,可以指定多个键)
比较顺序:(由小到大),处理不同类型的数据是有顺序的,有时候一个键值有多个类型,其键排序是有序的
最小值,null,数字,字符串,对象/文档,数组,二进制,对象id,布尔型,日期型,时间戳,正则表达式,最大值
避免使用skip略过大量结果
略过少量文档是不错的,但是略过大量文档就有性能问题(所有数据库都这样),所以推荐使用内置查询条件或者利用上次查询结果来进行下次查询
不用skip分页(传统分页不说了,应该都知道)
//first page var page1=db.foo.find().sort({"date":-1}).limit(100) //display first page while(page1.hasNext()){ latest=page1.next(); display(latest); } //next page var page2=db.foo.find({"date":{"$gt":lastest.date}}); page2.sort({"date":-1}).limit(100);
随机选取文档
一般方法得到文档总数,然后随机,但是计算总数非常会很耗时,所以采用其他变通的方法,如向每个文档添加一个随机数,然后执行如下查询
var random=Math.random() result=db.foo.findOne({"random":{"$gt":random}}) //if nothing in result, reverse the condition if(result==null) { result=db.foo.findOne({"random":{"$lte":random}}) }
高级查询选项
架设执行如下查询:var cursor=db.foo.find({"foo":"bar"}).sort({"x":1}),执行机理为将查询包装到一个更大的文档中,{"$query":{"foo":"bar"},"$orderby":{"x":1}}
大部分驱动有辅助措施添加各种选项,如
$maxscan 最多扫描文档数量
$min 开始的条件
$max 结束的条件
$hint 采用哪个索引
$explain 获取查询细节(索引,数量,耗时),而不是真正执行
$snapshot 确保查询的结果是在查询那一刻的一致快照
获得一致结果
数据处理的结果一般是从Mongodb中取出数据,处理然后在存回库中
例如有把集合看做一大堆文档
1、2、3、……、n
把2提取出来进行了修改,变成了2',2'>2,并可能导致了文档体积增加而预留空间不足,于是吧2'放到了文档末尾,变成了
1、3、……、n、2'
游标继续向后获得文档,直到获取到2'!!
处理方法为:对查询执行快照,使用了"$shapshot"的时候,查询是针对不变的集合视图进行的。
游标内幕
在服务器端的游标视角,游标会消耗内存和其他资源,因此要合理释放游标。释放游标的一些情况包括:完成匹配结果的迭代,客户端不在作用域内,超过10分钟未使用
如果不想超时销毁游标,需要使用"immortal"函数