【问题标题】:mongodb: another "how to add a random record" threadmongodb:另一个“如何添加随机记录”线程
【发布时间】:2015-09-13 03:18:49
【问题描述】:

我在 StackOverflow 上遇到过很多同样的问题。没有提供有效的可靠解决方案,所以我们开始:

我需要从我的 MongoDB 数据库中大约 500 万个文档中以一种有效的方式随机选择一个文档。

我尝试获取.count 并使用.skip 来获取随机文档,但这需要将近三秒钟,而且效率非常非常低。

我无法对文档进行更改(例如添加“随机”)条目到每个文档或更改它们的 _id。

我尝试了添加带有增量 _id 的文档的解决方案(使用 .skip 选择一个随机的 _id 绕过),但这比我尝试在短时间内添加许多文档时带来的麻烦更多时间。

以增量方式添加数据或选择随机文档不应该这么难。我要么遗漏了一些常识,要么做错了什么,或者这就是事实......

想提出这个话题并得到你的回应。

【问题讨论】:

  • Random record from MongoDB 的可能重复项
  • 您提供的链接中没有解决方案,还有一些线程已过时。
  • 发布示例文档
  • @Zettam 该问题与您所问的问题完全相同,并且有十 (10) 个赞成的答案。当它没有适用于您的情况的解决方案时,stackoverflow 没有。当你认为它已经过时时,就给它一个赏金。
  • {"name":"John","content":"someLongStringHere","json":"someHueJsonObcHere"}

标签: node.js mongodb


【解决方案1】:

这是一种使用_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;

// work out a "random" _id value in the range:
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 值本质上更简单,但基本思想相同。

【讨论】:

  • 这个想法不错,但存在一些问题:在没有.limit() 的情况下调用toArray()(在您的最小/最大步骤中)将检索所有500 万条记录。我还建议使用ObjectId#getTimestamp()ObjectId#createFromTime() 而不是使用子字符串。
  • @robertklep OOps。 Typo 忘记了.limit(),感谢您的关注。在那些时间戳方法不可用的地方给出了 shell 示例。但是他们的实现代码基本上就是将十六进制字符串部分转换为数字。但是您基本上是正确的,因为这将是一种更简洁的编码方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-14
  • 1970-01-01
  • 1970-01-01
  • 2014-01-13
相关资源
最近更新 更多