Рельсы выбирают случайную запись - PullRequest
17 голосов
/ 04 сентября 2010

Я не знаю, смотрю ли я здесь не в том месте или как, но есть ли у активной записи метод для поиска случайного объекта?

Что-то вроде?

@user = User.random

Или ... ну, так как этот метод не существует, есть ли какой-то удивительный "Rails Way" для этого, я всегда, кажется, многословен. Я тоже использую mysql.

Ответы [ 8 ]

39 голосов
/ 04 сентября 2010

Большинство примеров, которые я видел, заканчивают тем, что подсчитывают строки в таблице, а затем генерируют случайное число, чтобы выбрать один. Это связано с тем, что альтернативы, такие как RAND(), неэффективны в том смысле, что они фактически получают каждую строку и присваивают им случайное число, или я прочитал (и я думаю, что они специфичны для базы данных).

Вы можете добавить метод, подобный тому, который я нашел здесь .

module ActiveRecord
  class Base
    def self.random
      if (c = count) != 0
        find(:first, :offset =>rand(c))
      end
    end
  end
end

Это сделает так, чтобы любая используемая вами модель имела метод с именем random, который работает так, как я описал выше: генерирует случайное число из числа строк в таблице, затем выбирает строку, связанную с этим случайным число. В общем, вы делаете только один выбор, который вы, вероятно, предпочитаете:)

Вы также можете взглянуть на этот плагин rails .

7 голосов
/ 03 октября 2011

Мы обнаружили, что смещения бегали очень медленно на MySql для большой таблицы. Вместо использования смещения вроде:

model.find(:first, :offset =>rand(c))

... мы обнаружили, что следующая техника работает более чем в 10 раз быстрее (фиксировано на 1):

max_id = Model.maximum("id")
min_id = Model.minimum("id")
id_range = max_id - min_id + 1
random_id = min_id + rand(id_range).to_i
Model.find(:first, :conditions => "id >= #{random_id}", :limit => 1, :order => "id")
4 голосов
/ 16 октября 2012

Попробуйте использовать метод Array's sample :

@user = User.all.sample(1)
3 голосов
/ 09 марта 2015

В Rails 4 я бы расширил ActiveRecord::Relation:

class ActiveRecord::Relation
  def random
    offset(rand(count))
  end
end

Таким образом, вы можете использовать области:

SomeModel.all.random.first # Return one random record
SomeModel.some_scope.another_scope.random.first
3 голосов
/ 04 сентября 2010

Я бы использовал именованную область. Просто добавьте это в свою модель пользователя.

named_scope :random, :order=>'RAND()', :limit=>1

Случайная функция не одинакова в каждой базе данных. SQLite и другие используют RANDOM(), но вам нужно будет использовать RAND() для MySQL.

Если вы хотите получить более одной случайной строки, попробуйте это.

named_scope :random, lambda { |*args| { :order=>'RAND()', :limit=>args[0] || 1 } }

Если вы позвоните User.random, по умолчанию он будет равен 1, но вы также можете позвонить User.random(3), если хотите более одного.

2 голосов
/ 29 июля 2011

Если вам нужна случайная запись, но только в рамках определенных критериев, вы можете использовать «random_where» из этого кода:

module ActiveRecord
  class Base
    def self.random
      if (c = count) != 0
        find(:first, :offset =>rand(c))
      end
    end

    def self.random_where(*params)
      if (c = where(*params).count) != 0
        where(*params).find(:first, :offset =>rand(c))
      end
    end

  end
end

Например:

@user = User.random_where("active = 1")

Эта функция очень полезна для отображения случайных товаров на основе некоторых дополнительных критериев

1 голос
/ 29 мая 2018

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

https://github.com/haopingfan/quick_random_records

Простое использование:

@user = User.random_records(1).take


Все остальные ответы плохо работают с большой базой данных, кроме этого гема:

  1. quick_random_records стоит всего 4.6ms всего.

enter image description here

  1. принятый ответ User.order('RAND()').limit(10) стоимость 733.0ms.

enter image description here

  1. offset стоимость захода на посадку 245.4ms всего.

enter image description here

  1. User.all.sample(10) стоимость захода на посадку 573.4ms.

enter image description here

Примечание. В моей таблице всего 120 000 пользователей. Чем больше у вас записей, тем больше будет разница в производительности.


ОБНОВЛЕНИЕ:

Выполнение на таблице с 550 000 строк

  1. Model.where(id: Model.pluck(:id).sample(10)) стоимость 1384.0ms

enter image description here

  1. gem: quick_random_records только стоимость 6.4ms всего

enter image description here

1 голос
/ 13 ноября 2014

Вот лучшее решение для получения случайных записей из базы данных.RoR обеспечивает все в простоте использования.

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

Backport of Array # sample basedна github.com/marcandre/backports/ от Marc-Andre Lafortune Возвращает случайный элемент или n случайных элементов из массива.Если массив пуст и n равно nil, возвращает nil.Если n передается и его значение меньше 0, возникает исключение ArgumentError.Если значение n равно или больше 0, возвращается [].

[1,2,3,4,5,6].sample     # => 4     
[1,2,3,4,5,6].sample(3)  # => [2, 4, 5]     
[1,2,3,4,5,6].sample(-3) # => ArgumentError: negative array size     
[].sample     # => nil     
[].sample(3)  # => []     

Вы можете использовать условие согласно вашему требованию, как показано в примере ниже.

User.where (active:true) .sample (5)

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

Для получения дополнительной помощи посетите: http://apidock.com/rails/Array/sample

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