MongoDB Cursor

在Oracle这样的关系数据库中,游标是SQL的一个内存工作区,其作用就是用于临时存储从数据库中提取的结果数据块。Oracle的游标在PL/SQL编写存储过程时会经常用到,可以用于一些特殊的控制和曹走。而MongoDB也提供了游标这个定义—当然MongoDB的”存储过程”就是一段javascript的代码块。

存储过程的创建

下面是一个简单的存过,插入一个100个文档的序列,y是一个1-100的随机数。

使用js.save保存存过到MongoDB中:

执行存过:

在y字段上建立索引

find()等命令返回了一个cursor。

索引的执行计划
虽然获得的文档数量少了,我猜想这里也是有个“回表”的操作,由于MongoDB没有Oracle中的统计信息、列直方图等信息,仅能以Oracle中的CBO模式来执行。但MongoDB是全内存操作,也很少涉及到获取大量查询的情形,这点开销可以忽略不计。

定制游标结果

这里显示的结果是以y的顺序排列的,如果要以x的顺序插入,就需要多结果进行排序。这就用到了cursor的sort函数。

另外的两个游标函数是limit(n) 取出前n个文档和skip(n) 跳过前n个文档来取结果。
这样组合使用就可以达到想要的效果,例如过滤前10行,然后显示前6行,并以x字段的降序排列。

排序顺序的比较

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

查询结果的分页

根据前面的limit和skip,很容易做到了分页的效果:

在skip大量文档的时候,这个性能是非常低效的。所以《MongoDB The definitive guide》需要在条件字段上建立索引,并加入一个条件变量:

获得随机文档
通过skip和limit(1)也可以实现获得随机文档的效果:

但是这里使用了skip,所以不是一个很高效的做法。按照前面的经验将其改成一个查询条件:

但是很可能随机到了maxvalue,所以还加一个if判断

这样如果result再为空,说明没有任何文档。

获得一致性结果

对一个大集合进行find( {} )的fetch时候,已经扫描过的文档有一个被update之后占用了更多的空间,需要进行迁移(relocate),而迁移往往会放到集合的末尾,这就导致了游标到达文档最后的时候,该文档又被fetch了一遍。
解决这个一致性问题可以使用快照技术($snapshot: truedb.collection.find().snapshot(): http://docs.mongodb.org/manual/reference/operator/meta/snapshot/),让结果产生为一个不变的视图。在这种情况下,不一致性的结果只会在集合被等待另一组结果的游标修改时产生。(没读懂啥意思=。= Inconsistencies arise only when the collection changes under a cursor while it is waiting to get another batch of results.

^^
Book: 《mongoDB – The definitive guide》
Reference: MongoDB Docs

Posted in Database, JavaScript, NoSQL.