Для начала давайте рассмотрим простой способ решения задачи. В монгоиде есть метод, который позволяет пропустить несколько записей или, другими словами, установить курсор для точки отсчета. Этот метод называется skip и ему можно передать количество записей, которые стоит пропустить. Если у нас есть коллекция с тремя записями, то чтобы получить вторую, можно сделать что-то наподобие этого Post.skip(1).first. Зная количество документов в коллекции, мы можем сделать сдвиг на случайное количество документов и начать читать оттуда:
proxy = Post.where(...) skip = rand(proxy.count - COUNT_OF_POSTS_TO_SHOW) @posts = proxy.skip(skip).limit(COUNT_OF_POSTS_TO_SHOW)
Если у вас не будет специальных условий по которым вы делаете выборку, то код будет выглядеть проще. Обычно, некоторые условия все таки будут присутствовать, такие как дата создания или статус. Данная выборка довольно таки случайна, но не совсем, так как мы выбираем случайным образом точку отсчета, а дальше все документы идут подряд. Возможно, кому-то подойдет и этот вариант случайности, особенно если нужно выбрать только одну запись. Но данный метод может быть абсолютно неприемлемым в случаях, когда мы выбираем товары, показывая таким образом товары из одинаковой категории или с одинаковой ценой (в зависимости от индексов коллекции)
Мое решение для получения абсолютно случайных записей было немного сложнее, но давало более корректные результаты. Для этого мне понадобилось добавить новое поле к коллекции из которой делалась выборка, я назвал его rand_order. В него мы записывали случайное число с плавающей точкой от 0 до 1. Наиболее аккуратный способ заполнения этого поля, это добавить before_save фильтр для модели, который может выглядеть таким образом:
def set_rand_order self.rand_order = (rand 0.0..1).round(15) unless rand_order end
Таким образом, каждый раз при сохранении объекта, мы проверяем заполнено ли значение для поля rand_order и заполняем его если оно пустое. Получение случайных записей теперь будет происходить гораздо проще:
Post.asc(:rand_order).limit(COUNT_OF_POSTS_TO_SHOW)
Стоит брать во внимание, что если вы применяете этот способ для уже существующей коллекции, которая содержит документы, то для них нужно сгенерировать случайные числа для поля rand_order. Это можно сделать в миграции и с учетом того, что мы сделали это в before_filter, вам достаточно вызвать метод save для каждого из объектов:
Post.all.each{|p| p.save}
ссылка на оригинал статьи http://habrahabr.ru/post/210706/
Добавить комментарий