Случайная запись из MongoDB - PullRequest
288 голосов
/ 13 мая 2010

Я хочу получить случайную запись из огромной (100 миллионов записей) mongodb.

Какой самый быстрый и эффективный способ сделать это? Данные уже есть, и нет поля, в котором я могу сгенерировать случайное число и получить случайную строку.

Есть предложения?

Ответы [ 27 ]

0 голосов
/ 19 марта 2013

Если у вас есть простой ключ идентификатора, вы можете сохранить все идентификаторы в массиве, а затем выбрать случайный идентификатор.(Рубиновый ответ):

ids = @coll.find({},fields:{_id:1}).to_a
@coll.find(ids.sample).first
0 голосов
/ 06 декабря 2013

Если вы используете mongoid, оболочку для документа к объекту, вы можете сделать следующее в Рубин. (Предполагается, что ваша модель - Пользователь)

User.all.to_a[rand(User.count)]

В моем .irbrc у меня есть

def rando klass
    klass.all.to_a[rand(klass.count)]
end

так что в консоли рельсов я могу сделать, например,

rando User
rando Article

для случайного получения документов из любой коллекции.

0 голосов
/ 25 марта 2011

Эффективно и надежно работает вот что:

Добавьте поле с именем «random» к каждому документу и присвойте ему случайное значение, добавьте индекс для поля random и выполните следующие действия:

Давайте предположим, что у нас есть коллекция веб-ссылок, называемых "ссылками", и мы хотим получить случайную ссылку с нее:

link = db.links.find().sort({random: 1}).limit(1)[0]

Чтобы та же ссылка не появлялась во второй раз, обновите ее случайное поле новым случайным числом:

db.links.update({random: Math.random()}, link)
0 голосов
/ 30 января 2014

Используя Map / Reduce, вы, безусловно, можете получить случайную запись, но не обязательно очень эффективно, в зависимости от размера результирующей отфильтрованной коллекции, с которой вы в итоге работаете.

Я протестировал этот метод с 50 000 документов (фильтр сокращает его примерно до 30 000), и он выполняется примерно за 400 мс на Intel i3 с 16 ГБ ОЗУ и жестким диском SATA3 ...

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 просто создает массив идентификаторов всех документов, соответствующих запросу. В моем случае я проверил это примерно с 30 000 из 50 000 возможных документов.

Функция Reduce просто выбирает случайное целое число от 0 до количества элементов (-1) в массиве, а затем возвращает _id из массива.

400 мс звучит как долгое время, и действительно, если у вас пятьдесят миллионов записей вместо пятидесяти тысяч, это может увеличить накладные расходы до такой степени, что они станут непригодными для использования в многопользовательских ситуациях.

Существует открытая проблема для MongoDB по включению этой функции в ядро ​​... https://jira.mongodb.org/browse/SERVER-533

Если бы этот «случайный» выбор был встроен в поиск по индексу вместо того, чтобы собирать идентификаторы в массив и затем выбирать один, это невероятно помогло бы. (иди голосуй!)

0 голосов
/ 24 марта 2019

Я использую этот способ

db.collection.aggregate(
   [ { $sample: { size: 5 } } ]
)

полная документация https://docs.mongodb.com/manual/reference/operator/aggregation/sample/

0 голосов
/ 12 мая 2019

вы также можете использовать shuffle-array после выполнения вашего запроса

var shuffle = require ('shuffle-array');

Accounts.find (QRY, функция (ERR, results_array) { newIndexArr = перетасовка (results_array);

0 голосов
/ 20 ноября 2014

Это работает хорошо, это быстро, работает с несколькими документами и не требует заполнения поля rand, которое в итоге заполнится само собой:

  1. добавить индекс в поле .rand в вашей коллекции
  2. используйте поиск и обновление, что-то вроде:
// 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)
  }
})

пс. Как найти случайные записи в mongodb вопрос помечен как дубликат этого вопроса.Разница в том, что этот вопрос явно задает одну запись, а другую - о получении случайного документа s .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...