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

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

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

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

Ответы [ 27 ]

3 голосов
/ 19 декабря 2015

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

  1. сначала получите все идентификаторы
  2. получить размер документов
  3. цикл получает случайный индекс и пропускает дубликаты

    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)
                });
            });
    
3 голосов
/ 23 декабря 2014

Мое решение по php:

/**
 * Get random docs from Mongo
 * @param $collection
 * @param $where
 * @param $fields
 * @param $limit
 * @author happy-code
 * @url happy-code.com
 */
private function _mongodb_get_random (MongoCollection $collection, $where = array(), $fields = array(), $limit = false) {

    // Total docs
    $count = $collection->find($where, $fields)->count();

    if (!$limit) {
        // Get all docs
        $limit = $count;
    }

    $data = array();
    for( $i = 0; $i < $limit; $i++ ) {

        // Skip documents
        $skip = rand(0, ($count-1) );
        if ($skip !== 0) {
            $doc = $collection->find($where, $fields)->skip($skip)->limit(1)->getNext();
        } else {
            $doc = $collection->find($where, $fields)->limit(1)->getNext();
        }

        if (is_array($doc)) {
            // Catch document
            $data[ $doc['_id']->{'$id'} ] = $doc;
            // Ignore current document when making the next iteration
            $where['_id']['$nin'][] = $doc['_id'];
        }

        // Every iteration catch document and decrease in the total number of document
        $count--;

    }

    return $data;
}
2 голосов
/ 26 февраля 2012

Я бы предложил использовать карту / уменьшить, где вы используете функцию карты, чтобы излучать только тогда, когда случайное значение выше заданной вероятности.

function mapf() {
    if(Math.random() <= probability) {
    emit(1, this);
    }
}

function reducef(key,values) {
    return {"documents": values};
}

res = db.questions.mapReduce(mapf, reducef, {"out": {"inline": 1}, "scope": { "probability": 0.5}});
printjson(res.results);

Вышеприведенная функция reduf работает, потому что из функции карты выдается только одна клавиша ('1').

Значение "вероятности" определяется в "области видимости" при вызове mapRreduce (...)

Использование mapReduce, подобного этому, также может быть использовано на зарезервированном БД.

Если вы хотите выбрать ровно n из m документов из БД, вы можете сделать это следующим образом:

function mapf() {
    if(countSubset == 0) return;
    var prob = countSubset / countTotal;
    if(Math.random() <= prob) {
        emit(1, {"documents": [this]}); 
        countSubset--;
    }
    countTotal--;
}

function reducef(key,values) {
    var newArray = new Array();
for(var i=0; i < values.length; i++) {
    newArray = newArray.concat(values[i].documents);
}

return {"documents": newArray};
}

res = db.questions.mapReduce(mapf, reducef, {"out": {"inline": 1}, "scope": {"countTotal": 4, "countSubset": 2}})
printjson(res.results);

Где "countTotal" (m) - это количество документов в БД, а "countSubset" (n) - это количество документов для извлечения.

При таком подходе могут возникнуть некоторые проблемы в защищенных базах данных.

2 голосов
/ 30 апреля 2015

Вы можете выбрать случайный _id и вернуть соответствующий объект:

 db.collection.count( function(err, count){
        db.collection.distinct( "_id" , function( err, result) {
            if (err)
                res.send(err)
            var randomId = result[Math.floor(Math.random() * (count-1))]
            db.collection.findOne( { _id: randomId } , function( err, result) {
                if (err)
                    res.send(err)
                console.log(result)
            })
        })
    })

Здесь вам не нужно тратить место на хранение случайных чисел в коллекции.

2 голосов
/ 17 апреля 2018

Используя Python (pymongo), агрегатная функция также работает.

collection.aggregate([{'$sample': {'size': sample_size }}])

Этот подход намного быстрее , чем выполнение запроса случайного числа (например, collection.find ([random_int])). Это особенно касается больших коллекций.

2 голосов
/ 17 мая 2010

Я бы предложил добавить случайное поле int для каждого объекта. Тогда вы можете просто сделать

findOne({random_field: {$gte: rand()}}) 

чтобы выбрать случайный документ. Просто убедитесь, что вы уверены, что Index ({random_field: 1})

1 голос
/ 30 мая 2016

Если вы используете мангуст, тогда вы можете использовать случайный мангуст мангуст-случайный

1 голос
/ 11 сентября 2013

Когда я столкнулся с подобным решением, я вернулся и обнаружил, что бизнес-запрос на самом деле был направлен на создание некоторой формы ротации представляемого инвентаря. В этом случае есть гораздо лучшие варианты, которые имеют ответы от поисковых систем, таких как Solr, а не от хранилищ данных, таких как MongoDB.

Короче говоря, с требованием «разумно вращать» контент, что мы должны делать вместо случайного числа во всех документах, это включать персональный модификатор q Score. Чтобы реализовать это самостоятельно, принимая во внимание небольшую группу пользователей, вы можете хранить документ для каждого пользователя, который имеет идентификатор продукта, число показов, количество кликов, дату последнего посещения и любые другие факторы, которые компания считает значимыми для вычисления показателя aq. модификатор. При извлечении набора для отображения, как правило, вы запрашиваете больше документов из хранилища данных, чем запрошено конечным пользователем, затем применяете модификатор q Score, берете количество записей, запрошенных конечным пользователем, а затем рандомизируете страницу результатов, крошечную установить, поэтому просто сортируйте документы на прикладном уровне (в памяти).

Если юниверс пользователей слишком велик, вы можете классифицировать пользователей по группам поведения и индексировать по группам поведения, а не по пользователю.

Если набор продуктов достаточно мал, вы можете создать индекс для каждого пользователя.

Я считаю, что этот метод намного эффективнее, но, что более важно, более эффективен при создании соответствующего, полезного опыта использования программного решения.

1 голос
/ 21 января 2014

ни одно из решений не помогло мне. особенно, когда есть много пробелов и набор мал. это сработало очень хорошо для меня (в php):

$count = $collection->count($search);
$skip = mt_rand(0, $count - 1);
$result = $collection->find($search)->skip($skip)->limit(1)->getNext();
0 голосов
/ 20 декабря 2018

Мой PHP / MongoDB сортировка / заказ по СЛУЧАЙНОМУ решению. Надеюсь, это кому-нибудь поможет.

Примечание: в моей коллекции MongoDB есть числовые идентификаторы, которые ссылаются на запись базы данных MySQL.

Сначала я создаю массив из 10 случайно сгенерированных чисел

    $randomNumbers = [];
    for($i = 0; $i < 10; $i++){
        $randomNumbers[] = rand(0,1000);
    }

В своей агрегации я использую оператор конвейера $ addField в сочетании с $ arrayElemAt и $ mod (modulus). Оператор модуля даст мне число от 0 до 9, которое я затем использую, чтобы выбрать число из массива со случайными числами.

    $aggregate[] = [
        '$addFields' => [
            'random_sort' => [ '$arrayElemAt' => [ $randomNumbers, [ '$mod' => [ '$my_numeric_mysql_id', 10 ] ] ] ],
        ],
    ];

После этого вы можете использовать сортировку Pipeline.

    $aggregate[] = [
        '$sort' => [
            'random_sort' => 1
        ]
    ];
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...