我想从一个巨大的集合(1亿条记录)中获得一个随机记录。
最快最有效的方法是什么?
数据已经在那里,没有字段可以生成随机数并获得随机行。
我想从一个巨大的集合(1亿条记录)中获得一个随机记录。
最快最有效的方法是什么?
数据已经在那里,没有字段可以生成随机数并获得随机行。
当前回答
如果你有一个简单的id键,你可以将所有的id存储在一个数组中,然后随机选择一个id。(Ruby回答):
ids = @coll.find({},fields:{_id:1}).to_a
@coll.find(ids.sample).first
其他回答
如果没有数据,这是很困难的。_id字段是什么?它们是mongodb对象id吗?如果是这样,你可以得到最大值和最小值:
lowest = db.coll.find().sort({_id:1}).limit(1).next()._id;
highest = db.coll.find().sort({_id:-1}).limit(1).next()._id;
然后,如果你假设id是均匀分布的(但它们不是,但至少这是一个开始):
unsigned long long L = first_8_bytes_of(lowest)
unsigned long long H = first_8_bytes_of(highest)
V = (H - L) * random_from_0_to_1();
N = L + V;
oid = N concat random_4_bytes();
randomobj = db.coll.find({_id:{$gte:oid}}).limit(1);
这工作得很好,它是快速的,适用于多个文档,不需要填充rand字段,它最终会填充自己:
向集合上的.rand字段添加索引 使用查找和刷新,如下所示:
// Install packages:
// npm install mongodb async
// Add index in mongo:
// db.ensureIndex('mycollection', { rand: 1 })
var mongodb = require('mongodb')
var async = require('async')
// Find n random documents by using "rand" field.
function findAndRefreshRand (collection, n, fields, done) {
var result = []
var rand = Math.random()
// Append documents to the result based on criteria and options, if options.limit is 0 skip the call.
var appender = function (criteria, options, done) {
return function (done) {
if (options.limit > 0) {
collection.find(criteria, fields, options).toArray(
function (err, docs) {
if (!err && Array.isArray(docs)) {
Array.prototype.push.apply(result, docs)
}
done(err)
}
)
} else {
async.nextTick(done)
}
}
}
async.series([
// Fetch docs with unitialized .rand.
// NOTE: You can comment out this step if all docs have initialized .rand = Math.random()
appender({ rand: { $exists: false } }, { limit: n - result.length }),
// Fetch on one side of random number.
appender({ rand: { $gte: rand } }, { sort: { rand: 1 }, limit: n - result.length }),
// Continue fetch on the other side.
appender({ rand: { $lt: rand } }, { sort: { rand: -1 }, limit: n - result.length }),
// Refresh fetched docs, if any.
function (done) {
if (result.length > 0) {
var batch = collection.initializeUnorderedBulkOp({ w: 0 })
for (var i = 0; i < result.length; ++i) {
batch.find({ _id: result[i]._id }).updateOne({ rand: Math.random() })
}
batch.execute(done)
} else {
async.nextTick(done)
}
}
], function (err) {
done(err, result)
})
}
// Example usage
mongodb.MongoClient.connect('mongodb://localhost:27017/core-development', function (err, db) {
if (!err) {
findAndRefreshRand(db.collection('profiles'), 1024, { _id: true, rand: true }, function (err, result) {
if (!err) {
console.log(result)
} else {
console.error(err)
}
db.close()
})
} else {
console.error(err)
}
})
ps.如何在mongodb问题中找到随机记录被标记为此问题的副本。不同之处在于,这个问题明确地询问单个记录,而另一个问题明确地询问随机文档。
我的PHP/MongoDB排序/顺序随机解决方案。希望这对大家有所帮助。
注意:我在我的MongoDB集合中有数字ID,引用一个MySQL数据库记录。
首先,我用10个随机生成的数字创建一个数组
$randomNumbers = [];
for($i = 0; $i < 10; $i++){
$randomNumbers[] = rand(0,1000);
}
在我的聚合中,我使用$addField管道操作符结合$arrayElemAt和$mod(模)。模数运算符将给我一个从0到9的数字,然后我用它从随机生成的数字数组中选择一个数字。
$aggregate[] = [
'$addFields' => [
'random_sort' => [ '$arrayElemAt' => [ $randomNumbers, [ '$mod' => [ '$my_numeric_mysql_id', 10 ] ] ] ],
],
];
在此之后,您可以使用Pipeline排序。
$aggregate[] = [
'$sort' => [
'random_sort' => 1
]
];
为了获得确定数量的无重复的随机文档:
first get all ids get size of documents loop geting random index and skip duplicated number_of_docs=7 db.collection('preguntas').find({},{_id:1}).toArray(function(err, arr) { count=arr.length idsram=[] rans=[] while(number_of_docs!=0){ var R = Math.floor(Math.random() * count); if (rans.indexOf(R) > -1) { continue } else { ans.push(R) idsram.push(arr[R]._id) number_of_docs-- } } db.collection('preguntas').find({}).toArray(function(err1, doc1) { if (err1) { console.log(err1); return; } res.send(doc1) }); });
您还可以使用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轴为查询添加第二个随机性维度。