MongoDB查询(4)——游标和分页[八]

一、游标

        数据库使用游标返回find的执行结果。客户端对游标的实现通常能够对最终结果进行有效控制,从shell中定义一个游标非常简单,就是将查询结果分配给一个变量(用var声明的变量就是局部变量),便创建了一个游标,如下所示:

> var cursor = db.users.find()

       这么做的好处就是可以一次查看一条结果。如果将上面查询结果放在全局变量中或者根本就没有放在变量中,MongoDB shell会自动迭代,自动显示最开始的若干文档。

可以使用游标的next()方法获得下一条数据。使用hasNext()方法查看游标里面是否还有数据。

参考实例一:while迭代游标

var cursor = db.users.find().limit(10);
while(cursor.hasNext()){
	user=cursor.next();
	print(user.name);
}

 参考实例二:forEach迭代游标

var cursor = db.users.find().limit(10);
cursor.forEach(function(user){
	print(user.name);
});

       调用find()方法时,shell并不立即查询数据库,而是等待真正开始要求获得结果时才发送查询,这样在执行之前可以给查询附加额外的选项。几乎游标对象的每个方法都返回游标本身,这样就可以按任意顺序组成方法链。例如,下面几种表达式是等价的。

> var cursor=db.users.find().sort({"age":1}).limit(10).skip(10)
> var cursor=db.users.find().skip(10).limit(10).sort({"age":1})

       此时上面的查询还没有向数据库发送请求,他们只是在构造查询。现在,假设我们执行如下操作:

> cursor.hasNext()

      这时,查询被发往服务器。shell立刻获得100条数据或者前4M数据(两种之间取小者),这样下次调用next或者hasNext时就不必再次连接服务器获取结果了。当客户端用光了第一组结果,shell会再一次联系数据库。

二、游标的生命周期

       看待游标有两种角度:“客户端游标”以及“客户端请求过去的服务器端游标”,在服务器端,游标消耗内存和其他资源。所以我们讨论就讨论服务器端的,客户端的没有什么意义。当客户端向服务器发起一次查询find()就代表在服务器端创建了一个游标,下面三种情况会让游标销毁。

  • 当游标遍历尽了以后,或者客户端发来消息要求终止,数据库会释放这些资源。
  • 当客户端的游标不在作用域内时,驱动程序会向服务器发送一条特别消息,让其销毁游标。
  • 当服务器端10分钟以内不对游标进行操作,即使客户端游标在作用域内或者还没有迭代完,数据库也会自动销毁游标。

三.、imit、skip和sort

  • limit:用来限制返回的结果,返回匹配文档的上限
  • skip:跳过前面多少个文档
  • sort:  排序的参数

这些选项必须在查询发送到服务器之前指定

参考实例:分页查询users集合的第二页每页10条记录,并指定对age进行升序

> var cursor=db.users.find().skip(10).limit(10).sort({"age":1})
> cursor.forEach(function(user){
... print("userName:"+user.name+" age:"+user.age);
... });

 返回结果如下:

userName:user2088 age:11
userName:user2212 age:12
userName:user2371 age:13
userName:user2655 age:14
userName:user2681 age:15
userName:user2855 age:16
userName:user3186 age:17
userName:user3332 age:18
userName:user3383 age:19
userName:user3465 age:20

 四、怎么对mongodb进行分页

      当使用skip略过少量文档还是不错的。但是要是数量非常多的话,skip会变得相当慢,例如想返回第10000页(每页20条记录),MongoDB必须先找到200000条记录,然后再抛弃199920条数据,这种分页需求在业内也有一个名词叫做“深分页”。大多数数据库都会再索引中保存更多的元数据,用于处理skip(Solr 4.7.1也引入了游标处理这种深分页),所以要尽量避免滤过太多数据。

既然MongoDB的skip不适合做深分页,那怎么做呢?

答:MongoDB能不能做深分页这取决于查询本身。

根据一般业务来讲,可以找到一种方法在不使用skip的情况下实现分页,这主要取决于查询本身。

参考实例

例如要按照"date"降序显示文档列表。可以按照如下方式获取结果的第一页:

> db.users.find().sort({"create":-1}).limit(10)

 然后利用上次查询的最后一个文档中的"date"值作为查询条件,来获取下一页:

var lastTime=users.last.date;--这个可以根据实际情况获得,我这里只是随便写写,让你感受到这种思想。

获取下一页:

db.users.find({"create":{"$gt":lastTime}}).sort("create":-1}).limit(10)

 这种分页查询中就没有了skip了。

优化深分页的核心思想(包括关系型数据库,以及其他NoSql数据库):减少当前查询在结果集里面存放的数据。