mongodb 随机查询

来源:互联网 发布:电子相册制作软件 编辑:程序博客网 时间:2024/04/30 04:08

mongodb的随机查询不像mysql一样可以利用rand()函数,取随机数查询数据,mongodb在查询中,并没有类似rand()的查询方法 (mongodb当前版本1.6.3)

题外话: mysql的rand在执行效率上有一定问题 sql 需要优化 详见

下面是一个具体的例子和解决方案

部分参照官网的mongodb cookbook 文章 The Random Attribute译过来的,官网废话太多了

假如你现在执行如下mongodb查询

1photos.find({"author":"John Doe"})

查询结果是获取author为John Doe的所有数据,假如你只是想获取随机的几条数据,你可以查询后,计算数据总条数,然后rand结果数组的几条,就可以实现了
但对于几十万数据或高频度查询来说,这种查询几乎是致命的.

所以在你向数据库里插入数据的时候,可以预先定义一个RA字段(随机属性字段),插入RA字段一个随机数,用随机数查询

1> db.docs.drop()
2> db.docs.save( { key : 1, ..., random : Math.random() } )
3> db.docs.save( { key : 1, ..., random : Math.random() } )
4> db.docs.save( { key : 2, ..., random : Math.random() } )
5... many moreinsertions with 'key : 2' ...
6> db.docs.save( { key : 2, ..., random : Math.random() } )
7...

Math.random()实现了js中的随机数方法, 如果你使用的是Math.random() ,会获取一个0~1之间的随机数

01> Math.random()
020.09635701317617418
03> Math.random()
040.9123326061270348
05> Math.random()
060.06640002172592852
07...
08{ "_id": ObjectId("4bfa81198cf5fc1002a42b91"),"key" : 2, "random": 0.23578915913357468}
09...
10{ "_id": ObjectId("4bfa81198cf5fc1002a42b93"),"key" : 2, "random": 0.8983254666113549 }

如果你使用了上面的解决方案,那么你还要给随机的字段的数据库索引,比如上面的random

1.随机查询单条记录
如果想随机查询单条记录,那么只需要变更一下请求,对RA字段添加适当的过滤条件. 下面尝试着查询跟RA字段中的值最接近的记录

1> rand = Math.random()
2> result = db.docs.findOne( { key : 2, random : { $gte : rand } } )
3> if ( result == null ) {
4>   result = db.docs.findOne( { key : 2, random : { $lte : rand } } )
5> }

1.取rand 随机数
2.查询random字段中大于rand的单条记录
3.判断有无数据,无数据则查询小于rand的单条记录

使用这种方法的时候,很重要的一点是建立数据库索引

1> db.docs.ensureIndex( { key : 1, random :1 } )

2. 对样本数据进行map / reduce 查询
如果数据和计算量过大,可以取样本数据,然后对结果的RA字段进行如上过滤处理

01> db.docs.drop()
02> for(i=0; i < 10000; i++) { db.docs.save( { key : i % 10, rand : Math.random() } ) }
03> m = function() { emit(this.key, 1); }
04> r = function(k, vals) {
05  var sum=0;
06  for(var i in vals) sum += vals[i];
07  returnsum;
08 }
09> sample = 0.1
10> res = db.docs.mapReduce(m, r, { query : { key : 2, rand : { $lte: sample } } })

mongodb 将会执行query查询,只有符合条件的10%数据才会执行mapper方法,相较于全文查询,运行时间会大大减少
我本地测试查询key为2 ,且RA字段小于0.1的数据计数为98 很接近100 官网的数据为85,本地结果如下

01{
02    "result": "tmp.mr.mapreduce_1288863869_7",
03    "timeMillis": 25,
04    "counts": {
05        "input": 98,
06        "emit": 98,
07        "output": 1
08    },
09    "ok": 1,
10}

默认情况下map/reduce生成的collection是临时的,只要你断掉本次连接就会自动删除,所以查询结果,仅当次连接有效

.

01> res = db.docs.mapReduce(m, r, { query : { rand : { $lte : 0.1 } } })
02 
03#mongodb 执行查询语句,根据查询条件获取10%的样本数据 , 根据上述的查询,执行结果应该类似如下,本地测试数据
04...
05> db[res.result].find()
06{ "_id": 0, "value" : 98 }
07{ "_id": 1, "value" : 103 }
08{ "_id": 2, "value" : 89 }
09{ "_id": 3, "value" : 99 }
10{ "_id": 4, "value" : 100 }
11{ "_id": 5, "value" : 115 }
12{ "_id": 6, "value" : 103 }
13{ "_id": 7, "value" : 92 }
14{ "_id": 8, "value" : 111 }
15{ "_id": 9, "value" : 108 }

在map/reduce 查询随机数据的实例中,可以不必像第一种方法为属性创建库索引

附加说明
使用Math.random()插入数据进行查询是,概率的数会偏大,所以不一定能保证查询的结果是按照概率均匀分布的

1> db.docs.save( { key : 1, random : Math.random() } )
2> db.docs.save( { key : 1, random : Math.random() } )
3> db.docs.find()
4{ "_id": ObjectId("4bfa9585cffdb770c08e7cc9"),"key" : 1, "random": 0.9988383572723725 }
5{ "_id": ObjectId("4bfa9586cffdb770c08e7cca"),"key" : 1, "random": 0.8