2015-04-15 5 views
1

Предположим, у вас есть модель для элементов с атрибутом голосов. Здесь может быть какой-то выборка данных:Сортируйте запрос базы данных по столбцу, а затем произвольно

Item, Votes 
----------- 
Red  4 
Blue 5 
Black 5 
Green 4 
Cyan 5 
Yellow 4 
Orange 4 

Я хочу, чтобы область применения, что сначала сортирует их по голосам:

Item, Votes 
----------- 
Orange 4 
Green 4 
Yellow 4 
Red  4 
Blue 5 
Black 5 
Cyan 5 

А потом рандомизирует его в каждой группе голосов:

Item, Votes 
----------- 
Green 4 
Red  4 
Orange 4 
Yellow 4 
Black 5 
Blue 5 
Cyan 5 

И тогда ограничивает его двумя первыми результатами.

Я попытался это:

scope :options, -> { order('random()').order("votes asc").limit 2 } 

и это:

scope :options, -> { order("votes asc").order('random()').limit 2 } 

Но ни работает, как ожидалось. Первая версия кажется абсолютно случайной, а вторая версия не является случайной.

+0

Когда вы говорите, «первые два результата», вы имеете в виду первый результата от каждой группы (4 и 5 голосов)? Если вы имеете в виду первые два из младшей группы, ваше второе решение работает: 'scope: options, -> {order ('votes ASC, random()'). Limit (2)}' – Drenmi

ответ

0

В модели элементов я сделал этот метод:

def rand_value 
    self.votes + Random.rand 
end 

А потом в контроллере я использую этот запрос:

@items = @experiment.items.sort_by { |item| [item.rand_value] } 

Это, кажется, работает, как я хотел. Первые пункты в списке всегда имеют наименьшее количество голосов, но в каждой когорте они рандомизированы. Единственным недостатком этого является то, что он не ограничивается двумя результатами.

0

Что касается сортировки, есть большой драгоценный камень под названием «Ransack» (https://github.com/activerecord-hackery/ransack), который позволяет динамически сортировать ваши наборы данных. Боюсь, я не могу предложить многого на пути к вашим другим проблемам, но, надеюсь, это поможет в сортировке!

1

В зависимости от того, что вы хотите, у вас есть два варианта. Если вы хотите, чтобы первые два в полном результате (выделено ниже), ваше второе решение работает. Однако, если вы хотите, чтобы первая строка для каждой группы, вам необходимо изменить свой запрос несколько (как показано ниже :)


Первые строки полного результата:

scope :options, -> { order('votes ASC, random()').limit(2) } 

будет выберите следующие строки:

Item, Votes 
----------- 
Green 4 * 
Red  4 * 
Orange 4 
Yellow 4 
Black 5 
Blue 5 
Cyan 5 

F рвая строка каждой группы:

scope :options, -> { select('DISTINCT ON (votes) *').order('votes ASC, random()').limit(2) } 

будет выбрать следующие строки:

Item, Votes 
----------- 
Green 4 * 
Red  4 
Orange 4 
Yellow 4 
Black 5 * 
Blue 5 
Cyan 5