2013-04-16 2 views
1

У меня есть модель Post с некоторыми ассоциациями has_many.Пользовательский заказ в запросе Rails

class Post < ActiveRecord::Base 
... 
has_many :votes 
has_many :comments 
has_many :ratings 
end 

Я хочу, чтобы запрос, который упорядочивает сообщения (votes.count + comments.count + ratings.count).

Например, если у меня был 3 голоса, 2 комментария и 1 рейтинг, то его «метрический» порядок будет иметь значение 6. Как мне это сделать?

Я также хочу получить второй запрос, который заказывает его с теми же 3 параметрами (голосов, комментариев, оценок), но также добавляет 4-й параметр, который обратно пропорционален created_at, поэтому более новые должности будут оцениваться высоко, и более старые сообщения будут ранжироваться ниже. Таким образом, показатель упорядочения будет примерно таким:

(F*(1/created_at) + votes.count + comments.count + ratings.count), где F - коэффициент масштабирования. Как мне это сделать?

ответ

3

Речь идет об алгоритме.

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

Предлагаю вам построить еще одно поле под названием «оценка» для хранения вычисленного результата. Он имеет начальное значение при создании записи. Затем каждый раз, когда вы обновляете один из факторов - голосов, комментариев, оценок, вы вызываете крючок, чтобы снова вычислить «счет».

Когда ваш алгоритм изменился, вы устраиваете работника для вычисления «оценки» для всех записей снова.

Для заказа просто закажите их по «оценке».

3

Я бы рекомендовал вам использовать AR counter cache здесь:

4.1.2.4: counter_cache

Опция :counter_cache может быть использован для нахождения числа принадлежащих объектов более эффективным.
[...]
Хотя опция :counter_cache указана в модели, которая включает объявление belongs_to, фактический столбец должен быть добавлен в соответствующую модель.

Таким образом, вы бы изменить соответствующие belongs_to декларации включить :counter_cache вариант:

class Vote < ActiveRecord::Base 
    belongs_to :post, :counter_cache => true 
end 
# Similarly for the other two... 

, а затем добавить счетчик столбцы в таблице posts в миграции:

def change 
    change_table :posts do |t| 
    t.integer :votes_count 
    #... 
    end 
end 

You» Также хочу выполнить миграцию для инициализации счетчиков для существующих Post.

Тогда вы будете иметь счетчики как свойства ваших моделей, и вы можете сказать что-то вроде:

Post.where(...).order('posts.votes_count + posts.comments_count + posts.ratings_count') 

Если вы хотите включить created_at, то вы можете использовать extract(epoch from created_at), чтобы получить временную метку в качестве удобной двойной точности значение, которое вы можете использовать в арифметических выражениях.


Недостатком является то, что счетчики могут выйти из синхронизации, если вы отклониться, но волосы от Единого Истинного Пути к рельсам нирваны (или где-либо это действительно происходит;), так что вы должны будете будьте осторожны, чтобы не касаться базы данных самостоятельно и всегда проходите через ассоциации, чтобы создавать и уничтожать вещи. Я также рекомендую вам создать быстрый «грязный» метод проверки работоспособности, который вы можете запускать время от времени, чтобы убедиться, что счетчики правильные.

Если вы счастливы быть специфичными для PostgreSQL, тогда вы можете перехватить ерунду :counter_cache => true и всю хрупкость, которая приходит с ней, и использовать триггеры в базе данных для поддержания значений кешированных счетчиков.

1

Есть ли причина, почему это необходимо сделать в базе данных? Если нет, я бы предложил вам использовать метод sort_by ruby, после нахождения всех записей и их включенных ассоциаций. Что-то вроде:

# In the post model 
class Post < ActiveRecord::Base 
    def custom_metric 
    votes.size + comments.size + ratings.size 
    end 
end 

# In post controller 
@posts = Post.where(id: ..).includes(:votes, :comments, :ratings).sort_by(&:custom_metric) 

Вы можете следовать логике того же типа для другого способа сортировки объектов. Этот метод будет сравнительно быстрым для других предложенных и будет иметь бонус, не вызывающий денормализации данных. Запрос всегда возвращает желаемый результат независимо от состояния вашей базы данных.

+1

Я согласен с вашим ответом в некоторой степени, но в случаях с большим количеством сообщений, где вы хотите сделать разбиение на страницы или просто получить верхние результаты N, тогда извлечение всех записей из базы данных может быть проблематичным – Lummo

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