Как ранжироваться в Rails после группировки - PullRequest
1 голос
/ 11 июля 2020

В моем приложении Rails 6, где я использую Postgres, у меня есть таблица с именем UserCategories.

| id | user_id | category_id | points| rank |

Я пытаюсь сделать следующее:

  1. Группировать записи по category_id
  2. Сортировать записи для каждого category_id по баллам (des c)
  3. Обновлять поле ранга в соответствии с порядком, в котором запись находится для category_id

Пример (желаемый ранг определяется количеством очков за category_id):

| id | user_id | category_id | points| rank |
| 1  |    1    |    1        |   2   |     |  #  I want rank to be 1
| 2  |    2    |    1        |   1   |     |  #  I want rank to be 2
| 3  |    1    |    2        |   3   |     |  #  I want rank to be 1
| 4  |    2    |    2        |   3   |     |  #  I want rank to be 1

Метод моей модели:

  def self.calculate_user_category_ranks
    @user_categories = UserCategory.select(:id, :points, :user_id, :category_id, :rank).all.order(points: :desc).group_by(&:category_id)
    # returns: 
    #   {2=>[#<UserCategory:0x000000000de8be00 id: 2, user_id: 1, category_id: 2, points: 3, rank: 0>, #<UserLeague:0x000000000de8bce8 id: 4, user_id: 2, category_id: 2, points: 3, rank: 0>],
         1=>[#<UserCategory:0x000000000de8bbf8 id: 1, user_id: 1, category_id: 1, points: 2, rank: 0>, <UserLeague:0x000000000de8bb30 id: 3, user_id: 2, category_id: 1, points: 1, rank: 0>]}

    rank = 0
    points_counter = 0

    @user_categories.each do |id, points|
      uc = UserCategory.find(id)
      
      if points != point_counter
        rank += 1
        point_counter = points
      end

      uc.rank = rank
      uc.save
    end
  end

После выполнения этого кода:

| id | user_id | category_id | points| rank |
| 1  |    1    |    1        |   2   |  2  |  #  I want rank to be 1
| 2  |    2    |    1        |   1   |  0  |  #  I want rank to be 2
| 3  |    1    |    2        |   3   |  1  |  #  I want rank to be 1
| 4  |    2    |    2        |   3   |  0  |  #  I want rank to be 1

Может ли кто-нибудь помочь мне определить, что я делаю не так?

Ответы [ 2 ]

0 голосов
/ 11 июля 2020

Начните с

UserCategory.group(:category_id)

Затем создайте его

Если вам действительно нужно ограничить возвращаемые поля или добавить предложение where, тогда

UserCategory.where(some_field: "some_value").select(:id, :points, :user_id, :category_id, :rank).group(:category_id).order(:points :desc)

https://apidock.com/rails/ActiveRecord/QueryMethods/group подробнее

0 голосов
/ 11 июля 2020

Вы можете выбрать rank в качестве столбца базы данных из соображений эффективности, но принципы нормализации базы данных предполагают, что иметь столбец, значение которого вычисляется из других столбцов в таблице, является «плохой практикой». стол. Итак, признавая, что вы можете не принять это решение по соображениям эффективности, позвольте мне предложить, чтобы для любого конкретного экземпляра UserCategory вы могли определить его ранг в Ruby:

class UserCategory < ApplicationRecord
  scope :in_the_same_category, ->(category_id) { where("category_id = ?", category_id }

  def in_my_category
    UserCategory.in_the_same_category(category_id)
  end

  def rank
    in_my_category.
      sort_by(&:points).
      reverse.
      map(&:points).
      uniq.
      index(points) + 1
  end
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...