不厚

博客园 首页 新随笔 联系 订阅 管理

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"函数

posted on 2012-12-15 04:47  飞宏风  阅读(276)  评论(0编辑  收藏  举报