mongodb查询 游标 php,Mongodb 查询&游标

1.find方法介绍

在不传入参数的情况下,find方法缺省使用{}做参数,它匹配所有的document。

我们可以传入一个查询document给find方法来限制输出,如:查找年龄为27的用户

> db.users.find({"age" : 27})

一个查询document里可以包括多个条件,如:查询年龄为27并且名字叫joe的用户

> db.users.find({"username" : "joe", "age" :

27})

条件之间都是And关系。

指定查询返回的内容

有时候我们并不需要返回document里所有的key/value对,我们可以给find(或findOne)方法传入第二个参数来指定返回哪些key的值。

比如:我们只想要用户的名字和Email

> db.users.find({}, {"username" : 1, "email" :

1})

{

"_id" : ObjectId("4ba0f0dfd22aa494fd523620"),

"username" : "joe",

"email" : "joe@example.com"

}

"_id"键总是会被返回。

如果想指定不想返回的key,如:返回除了fatal_weakness以外的key的值

> db.users.find({}, {"fatal_weakness" :

0})

用这个方法可以把缺省返回的"_id"给排除掉

> db.users.find({}, {"username" : 1, "_id" :

0})

{

"username" : "joe",

}

限制

查询document里边key的值对数据库来说必须是个常量,假如collection里边有两个key,一个叫int_stock一个叫num_sold,

如果我们想查询这两个key相等的document,下边的查询是行不通的

> db.stock.find({"in_stock" :

"this.num_sold"})/ doesn't work

2.查询条件

"$lt", "$lte", "$gt",

和"$gte"分别对应,>=

如:查询年龄在18到30之间的用户

> db.users.find({"age" : {"$gte" : 18, "$lte"

: 30}})

“$ne" 不等于,可以用于任意类型的数据

如,查找名字不叫joe的用户

> db.users.find({"username" : {"$ne" :

"joe"}})

OR查询

mongoDB里有两种使用OR的方法,如果是一个key对应的多个值,可以用"$in",还有个”or"是一种更通用的方法。

如:我们在进行一个抽奖活动,ticket

No是725,542,390的三个中奖了,我们可以这样把这三个document查出来

> db.raffle.find({"ticket_no" : {"$in" : [725,

542, 390]}})

"$in"里边指定的值可以是不同的数据类型

与"$in"相反的操作就是"$nin”,返回指定的key的值不存在于数组里边的document,

> db.raffle.find({"ticket_no" : {"$nin" :

[725, 542, 390]}})

上边的查询返回没中奖的所有ticket。

如果要在多个key的查询条件之间建立Or关系就得用"$or",如,查询彩票号码为725,

542, 390  或者winner为true的document

> db.raffle.find({"$or" : [{"ticket_no" :

{"$in" : [725, 542, 390]}},{"winner" : true}]})

$not

"$not"可以应用于上边说的所有的条件操作符上,举个例子之前我们先看下求余(取模)操作符"$mod","$mod"指定一个整数数组做为key的值,

里边两个数字,第一个是对其求余的数字,第二个是余数。比如,我们要找id_num的值对5求余,余数为1的document

> db.users.find({"id_num" : {"$mod" : [5,

1]}})

现在我们使用$not,如果找id_num对5求余后余数不为1的document

> db.users.find({"id_num" : {"$not" : {"$mod"

: [5, 1]}}})

规律

回头再看下前一章的更新修饰符,就会发现同样是$打头的符号,它和本章说的查询条件符号的位置是不一样的,

在查询里,"$lt"属于内层document,而更新的时候"$inc"是外层document的key。

一个key上可以有多个查询条件,却不能有多个更新。

查询年龄为20到30的用户可以这样做

> db.users.find({"age" : {"$lt" : 30, "$gt" :

20}})

但是却没有下边这种更新修饰符

{"$inc" : {"age" : 1}, "$set" : {age : 40}}

3.特殊类型查询

null

null同时匹配自身和不存在的key,

> db.c.find()

{ "_id" : ObjectId("4ba0f0dfd22aa494fd523621"), "y" : null

}

{ "_id" : ObjectId("4ba0f0dfd22aa494fd523622"), "y" : 1

}

{ "_id" : ObjectId("4ba0f148d22aa494fd523623"), "y" : 2

}

如果要查找y为null的document

> db.c.find({"y" : null})

{ "_id" : ObjectId("4ba0f0dfd22aa494fd523621"), "y" : null

}

查找key为null的document时,缺少此key的所有document也会返回

> db.c.find({"z" : null})

{ "_id" : ObjectId("4ba0f0dfd22aa494fd523621"), "y" : null

}

{ "_id" : ObjectId("4ba0f0dfd22aa494fd523622"), "y" : 1

}

{ "_id" : ObjectId("4ba0f148d22aa494fd523623"), "y" : 2

}

如果想查找key存在并且为null的document,就要用"$exists"符号,

> db.c.find({"z" : {"$in" : [null], "$exists"

: true}})

这个办法看起来比较笨,没办法,我们没有"$eq"条件符号。

正则表达式

查找名字叫joe,但是忽略大小写的document

> db.users.find({"name" :joe/i})

mongoDB使用Perl Compatible Regular Expression

(PCRE)来匹配正则表达式,所有PCRE允许的语法都可以在mongoDB里使用。

查询数组

查询数组很简单,通常情况下,数组的每个元素的值都能看作key的值。

例如,我们有一些水果

> db.food.insert({"fruit" : ["apple",

"banana", "peach"]})

我们查找水果里有香蕉的ducoment

> db.food.find({"fruit" : "banana"})

用起来就好像我们有这么一个document在

{"fruit" : "apple","fruit" : "banana", "fruit" :

"peach"}

当然,上边这个document是不合法的。

$all

如果要匹配数组里的多个元素,就要用“$all”。

例如,我们有这样一个collection

> db.food.insert({"_id" : 1, "fruit" :

["apple", "banana", "peach"]})

> db.food.insert({"_id" : 2, "fruit" :

["apple", "kumquat", "orange"]})

> db.food.insert({"_id" : 3, "fruit" :

["cherry", "banana", "apple"]})

要查询既有apple又有banana的document

> db.food.find({fruit : {$all : ["apple",

"banana"]}})

{"_id" : 1, "fruit" : ["apple", "banana", "peach"]}

{"_id" : 3, "fruit" : ["cherry", "banana", "apple"]}

$all的值里边的元素是没有顺序的,只要目标包含里边的每个元素即可,如果不使用$all,就会执行精确的匹配检查。

> db.food.find({"fruit" : ["apple", "banana",

"peach"]})

上边这个查询会匹配第一个document

> db.food.find({"fruit" : ["apple",

"banana"]})

这个查询就不会匹配第一个document

> db.food.find({"fruit" : ["banana", "apple",

"peach"]})

同样,这个查询也不会匹配第一个document

如果要匹配数组里指定的元素,就要使用key.index这种语法

> db.food.find({"fruit.2" : "peach"})

index 是从0开始索引的

$size

按照数组中元素的个数查询

> db.food.find({"fruit" : {"$size" :

3}})

$size

不能和比较条件符号(如$gt)联合使用

$slice操作符

上边曾经提到过,使用find方法的第二个参数,指定哪些键值会被返回,$slice操作符可以返回数组元素的一个子集。

例如,查找博客和它的前10条评论

> db.blog.posts.findOne(criteria, {"comments"

: {"$slice" : 10}})

或者,如果想要后10条评论的话

> db.blog.posts.findOne(criteria, {"comments"

: {"$slice" : -10}})

也可以返回中间的一段结果

> db.blog.posts.findOne(criteria, {"comments"

: {"$slice" : [23, 10]}})

上边的查询跳过前23个元素,返回第24个到第34个元素。

查询嵌入的document

查询嵌入的整个document和普通的查询没有差别,比如我们有这样一个document

{

"name" : {

"first" : "Joe",

"last" : "Schmoe"

},

"age" : 45

}

那么我们就可以这样查询名字叫Joe

Schmoe的人

> db.people.find({"name" : {"first" : "Joe",

"last" : "Schmoe"}})

如果Joe要加一个middle

name,这个查询就不行了,这种查询必须匹配整个嵌入的document,而且key是有顺序的。

我们可以用.来直接查询嵌入的key。

> db.people.find({"name.first" : "Joe",

"name.last" : "Schmoe"})

点号在查询用document里就被解释为“通向嵌入的document”,所以document的key里边不能包含点号。

$where 查询

$where子句允许你在查询里执行arbitary

javascript,使你几乎可以在查询中做任何事情。

最常见的例子就是比较document里边两个key的值。举个例子,我们有个list,我们想返回里边的key的值里有相等的document

(随便哪两个key,只要它们的value相等即可)。

> db.foo.insert({"apple" : 1, "banana" : 6,

"peach" : 3})

> db.foo.insert({"apple" : 8, "spinach" : 4,

"watermelon" : 4})

第二个document里,菠菜和西瓜的值是相等的,这个应该返回,这个使用$条件查询符号是做不到的。

> 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;

... }});

如果函数返回true,那么这个document就会作为结果集的一部分被返回。

刚才我们定义了一个函数,给$where查询指定一个字符串是一样的效果

> db.foo.find({"$where" : "this.x + this.y ==

10"})

> db.foo.find({"$where" : "function() { return

this.x + this.y == 10; }"})

这两个查询是等价的。

如非必要,尽量不要使用$where查询,$where查询比一般的查询慢很多,每个document都必须从BSON转换为一个javascript对象,

然后执行$where表达式。而且,索引也不能使用。你可以通过组合使用$where查询和非$where查询来降低使用它的代价。

4.游标

find方法使用游标返回查询结果,游标的客户端实现使你可以对最终结果做很多的控制。在shell里创建一个游标很简单,往collection里放些document,

执行查询,将返回结果指派给一个本地变量即可。

> for(i=0; i<100; i++)

{

... db.c.insert({x : i});

... }

> var cursor = db.collection.find();

你可以使用next方法来遍历结果,使用hasNext方法来检查有没有下一个,典型的循环如下

> while (cursor.hasNext()) {

... obj = cursor.next();

.../ do stuff

... }

cursor类同样实现了iterator接口,所以你可以使用forEach循环

> var cursor = db.people.find();

> cursor.forEach(function(x) {

... print(x.name);

... });

adam

matt

zak

当你调用find方法的时候,shell并不会立刻去查询数据库,直到你真正请求结果的时候才发送查询,这样你可以在实际执行查询之前

追加一些其他的选项,游标的这些方法几乎都是返回游标本身,所以你可以按任意顺序链入这些方法,下边三个查询是等价的

> var cursor = db.foo.find().sort({"x" :

1}).limit(1).skip(10);

> var cursor =

db.foo.find().limit(1).sort({"x" : 1}).skip(10);

> var cursor =

db.foo.find().skip(10).limit(1).sort({"x" : 1});

在这个时候查询并未执行,所有的函数都只是构建查询。现在我们调用hasNext方法,

> cursor.hasNext()

这时候查询被送到服务器,shell就立刻取到了前100条结果或者前4MB的结果,所以接下来在调用next方法的时候就不会再发送请求

以及接受结果,当第一次返回的结果集用完的时候,shell会再次联系服务器,请求更多的结果。

Limits, Skips, 和Sorts

limit函数限制返回的结果集的上限,如,只返回3个结果

> db.c.find().limit(3)

skip函数跳过前x个结果,返回剩余的

> db.c.find().skip(3)

sort方法使用一组键值对做参数,key是document里的key的名字,value是1升序或者-1降序。

如,按名字升序和年龄降序排序

> db.c.find().sort({username : 1, age :

-1})

比较顺序

mongoDB有一个关于各种数据类型之间比较的等级制度。在某些情况下,你可能有一个key,它的值有多种类型,如果你想按照该

key排序,mongoDB有一个预定义好的顺序,它们从小到大分别为:

1. Minimum value

2. null

3. Numbers (integers, longs, doubles)

4. Strings

5. Object/document

6. Array

7. Binary data

8. Object ID

9. Boolean

10. Date

11. Timestamp

12. Regular expression

13. Maximum value

获取一致性的结果

处理数据的常见方式就是从mongoDB里取出来,然后修改它,再存储进mongoDB,如下:

cursor = db.foo.find();

while (cursor.hasNext()) {

var doc = cursor.next();

doc = process(doc);

db.foo.save(doc);

}

结果集数量很小的时候,这样子做是没问题的,如果结果集很大,这个做法就行不通了。

想象一下document是如何存储的,你可以像下图4-1一样来理解存储document的collection,每个雪花代表一个document,因为

它们和document一样,都很漂亮而且独一无二。

b421ecf7817702d705ace7fa4b0c24b3.png

图4-1

现在,我们执行一个find,它从头开始向右返回结果。程序获取前100个document并处理它们,然后将它们存储回数据库,如果一个

document没有足够的空间来容纳新的document,如图4-2,那么就需要给它重新指定存储位置。通常,这个新的document

会重新

定位到collection的尾部,如图4-3.

e4caa9985832bd92703280144fe4bf4a.png

图4-2

27d5d2d8e833099643b7735d9decc5c5.png

图4-3

现在,我们的程序持续不断地批量获取document,当它到达collect尾部的时候,那些被重新定位的document会再次返回,如图4-4.

95ec8e6cdeb4f86099c43a00c0edec63.png

图4-4

这个问题的解决办法是,对查询使用snapshot,如果添加了“$snapshot"选项,查询就会按照collection未被改变的视图运行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值