我想从一个巨大的集合(1亿条记录)中获得一个随机记录。
最快最有效的方法是什么?
数据已经在那里,没有字段可以生成随机数并获得随机行。
我想从一个巨大的集合(1亿条记录)中获得一个随机记录。
最快最有效的方法是什么?
数据已经在那里,没有字段可以生成随机数并获得随机行。
当前回答
使用Map/Reduce,您当然可以获得一个随机记录,只是不一定非常有效,这取决于您最终使用的过滤集合的大小。
我已经用5万个文档测试了这个方法(过滤器将其减少到大约3万个),它在Intel i3、16GB ram和SATA3 HDD上执行大约400毫秒……
db.toc_content.mapReduce(
/* map function */
function() { emit( 1, this._id ); },
/* reduce function */
function(k,v) {
var r = Math.floor((Math.random()*v.length));
return v[r];
},
/* options */
{
out: { inline: 1 },
/* Filter the collection to "A"ctive documents */
query: { status: "A" }
}
);
Map函数简单地创建一个数组,其中包含所有与查询匹配的文档的id。在我的例子中,我测试了5万个可能的文档中的大约3万个。
Reduce函数只是在数组中从0到项数(-1)之间选择一个随机整数,然后从数组中返回该_id。
400ms听起来是一段很长的时间,而且确实如此,如果您有5000万条记录而不是5万条记录,这可能会增加开销,以至于在多用户情况下无法使用。
MongoDB在核心中包含这个功能有一个悬而未决的问题…https://jira.mongodb.org/browse/SERVER-533
如果将这种“随机”选择构建到索引查找中,而不是将id收集到一个数组中然后选择一个,这将非常有帮助。(去投票吧!)
其他回答
如果你有一个简单的id键,你可以将所有的id存储在一个数组中,然后随机选择一个id。(Ruby回答):
ids = @coll.find({},fields:{_id:1}).to_a
@coll.find(ids.sample).first
下面是一种使用_id的默认ObjectId值和一些数学和逻辑的方法。
// Get the "min" and "max" timestamp values from the _id in the collection and the
// diff between.
// 4-bytes from a hex string is 8 characters
var min = parseInt(db.collection.find()
.sort({ "_id": 1 }).limit(1).toArray()[0]._id.str.substr(0,8),16)*1000,
max = parseInt(db.collection.find()
.sort({ "_id": -1 })limit(1).toArray()[0]._id.str.substr(0,8),16)*1000,
diff = max - min;
// Get a random value from diff and divide/multiply be 1000 for The "_id" precision:
var random = Math.floor(Math.floor(Math.random(diff)*diff)/1000)*1000;
// Use "random" in the range and pad the hex string to a valid ObjectId
var _id = new ObjectId(((min + random)/1000).toString(16) + "0000000000000000")
// Then query for the single document:
var randomDoc = db.collection.find({ "_id": { "$gte": _id } })
.sort({ "_id": 1 }).limit(1).toArray()[0];
这是shell表示法的一般逻辑,很容易适应。
所以在点上:
查找集合中的最小和最大主键值 生成一个位于这些文档的时间戳之间的随机数。 将随机数与最小值相加,然后找到大于或等于该值的第一个文档。
这使用了从“十六进制”的时间戳值中“填充”来形成有效的ObjectId值,因为这就是我们正在寻找的。使用整数作为_id值本质上更简单,但在点中基本思想相同。
您还可以使用MongoDB的地理空间索引功能来选择与随机数“最近”的文档。
首先,在集合上启用地理空间索引:
db.docs.ensureIndex( { random_point: '2d' } )
用x轴上的随机点创建一堆文档:
for ( i = 0; i < 10; ++i ) {
db.docs.insert( { key: i, random_point: [Math.random(), 0] } );
}
然后你可以像这样从集合中随机获得一个文档:
db.docs.findOne( { random_point : { $near : [Math.random(), 0] } } )
或者你可以检索几个文档最近的随机点:
db.docs.find( { random_point : { $near : [Math.random(), 0] } } ).limit( 4 )
这只需要一个查询,没有空检查,加上代码干净,简单和灵活。您甚至可以使用地理点的y轴为查询添加第二个随机性维度。
对于我来说,我想以随机顺序获得相同的记录,所以我创建了一个用于排序的空数组,然后生成1到7之间的随机数(我有7个字段)。每次我得到一个不同的值,我分配一个不同的随机排序。 这是“外行”,但对我来说很管用。
//generate random number
const randomval = some random value;
//declare sort array and initialize to empty
const sort = [];
//write a conditional if else to get to decide which sort to use
if(randomval == 1)
{
sort.push(...['createdAt',1]);
}
else if(randomval == 2)
{
sort.push(...['_id',1]);
}
....
else if(randomval == n)
{
sort.push(...['n',1]);
}
MongoDB现在有$rand
要选择n个非重复项,请使用{$addFields: {_f: {$rand:{}}}}进行聚合,然后按_f进行$sort和$limit n。