2010-09-04 3 views
17

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

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

@user = User.random 

Или ... ну так что метод не существует есть некоторые удивительные «Rails Way» делать это, я всегда, кажется, быть многословным. Я также использую mysql.

ответ

3

В Rails 4 I продлит 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

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

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

Я слышал, что RAND() очень медленный, потому что он сначала выбирает каждую запись, а затем как-то выбирает один, но я, вероятно, ошибаюсь. –

+1

@Blaenk: Он очень медленно работает на MySQL. Однако я не знаю о реализации. – Swanand

+1

+1 для создания области, мне тоже нравится. –

39

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

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

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

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

Вы также можете посмотреть this rails plugin.

+3

Это приятно. Мне это нравится, потому что он дополнительно не специфичен для ORM. Хорошая работа, спасибо! –

+0

в Rails 4.1, мне пришлось использовать: 'offset (rand (c)). First' –

2

Если вам потребуется случайную запись, но только в определенных критериях, которые Вы могли бы использовать «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") 

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

+0

Рельсы 3 и активное связывание отношений заботятся об этом случае. Вы можете сделать только 'User.random.where ('active = 1')' без необходимости в дополнительном глобальном методе. –

+1

Метод «find» не возвращает объект Relation, он возвращает либо объект модели, либо массив :), но! вы можете сделать это 'User.where ('active = 1'). random', и это будет работать отлично ... Я не знаю, почему я это пропустил :) –

+0

Вы правы. Хороший глаз. Я подумал одно и набрал еще один. :-) –

7

Мы обнаружили, что смещения побежали очень медленно на 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") 
+1

Последняя строка немного слишком многословна. Это делает то же самое (в Rails 3): 'Model.where (" id> = # {random_id} "). First' –

+3

Если какие-либо записи были удалены, то метод не генерирует равномерно распределенные результаты, что вообще то, чего можно было бы ожидать. Представьте себе случай, когда существуют идентификаторы 1-10, за исключением id 5. В этом случае модель с идентификатором 6 будет возвращена, когда генератор случайных чисел производит либо 5, либо 6 (20% времени), тогда как каждый из других существующих идентификаторов выбирается только в 10% случаев. Возможно, это не разбойник сделки, а то, что нужно знать. – encoded

4

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

@user = User.all.sample(1) 
+0

Мне нравится ваш ответ, это какой-то настоящий рубиновый стиль. – blawzoo

+16

Он определенно не будет масштабироваться. Вы будете раздувать всех пользователей в памяти ... – Nicholas

+0

Вы должны удалить (1), так как это приведет к получению одноэлементного массива. Использование User.all.sample приводит только к одному пользователю. – Danny

1

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

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

Backport of Array # образец на основе Marc-Andre Lafortune's github.com/marcandre/backports/ Возвращает случайный элемент или 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 (активный: истина) .Sample (5)

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

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

Смежные вопросы