2013-03-04 51 views
1

我有一個應用程序,可以爲使用game_type的遊戲玩遊戲的用戶提供積分。我想要做的是:如何顯示不同分類彙總中的最高排名

  1. 大部根據遊戲積分
  2. 判斷基礎上,他們多少分都爲game_id相比其他用戶
  3. 用戶的遊戲等級(1級=最高分)基於game_type
  4. 小計點確定用戶game_type基礎上,他們多少分都爲game_type_id相比其他用戶

基於查詢resul等級(等級1 =最高分)以上我只想顯示game_types和遊戲之間的排名前五位。

例如,讓我們說,計算是本作的用戶:

game_id = 1, rank 200 
game_id = 2, rank 10 
game_id = 3, rank 6 
game_id = 4, rank 31 

game_type_id = 1, rank 500 
game_type_id = 2, rank 400 
game_type_id = 3, rank 1 
game_type_id = 4, rank 7 
game_type_id = 5, rank 100 

那我就只想顯示遊戲排名:2,3,4和game_types:3,4,5因爲這些是該用戶的遊戲和game_types中的前五名。

我想過爲用戶表中的每個game_type和game(例如game_1_rank,game_2_rank,game_type_1_rank等)創建一個game_type和game field,以便我可以計算每小時後臺作業中的點數,然後試圖檢索但是我不認爲這是自新遊戲和game_types隨着時間的推移而增加的最佳方法。

因此,我認爲最好的方法是在用戶#show頁面加載並緩存該頁面(並每小時到期)時進行計算。

我的模式是這樣的:

user 
    has_many :points 

point 
    belongs_to :game 
    belongs_to :game_type 
    belongs_to :user 

game 
    has_many :points 
    has_one :game_type 

game_type 
    has_many :points 

我在用戶#這個代碼顯示計算總體排名爲所有的遊戲和game_types的,但我不知道如何調整它,所以我可以訪問我認爲的最高排名(在我看來,我還沒有代碼可以顯示這個用戶的前五名和他們的遊戲/遊戲類型)。

# calculate ranks for all users for all games in order to find the user's rank 
@games = Game.all 

@games.each do |game| 
    @users_by_game = Point.where(「game_id = ?」, game.id).select("sum(amount) as points, user_id").order("points desc").group("user_id") 
    rank = 0 
    points = 0 

    @users_by_game.each_with_index do |user_by_game, index|   
    if user_by_game.points != points 
     points = user_by_game.points 
     rank += 1 
    end 
end 

# calculate ranks for all users for all games_types in order to find the user's rank 
@game_types = GameType.all 

@game_types.each do |game_type| 
    @users_by_game_type = Point.where(「game_type_id = ?」, game_type.id).select("sum(amount) as points, user_id").order("points desc").group("user_id") 
    rank = 0 
    points = 0 

    @users_by_game_type.each_with_index do |user_by_game_type, index|   
    if user_by_game_type.points != points 
     points = user_by_game_type.points 
     rank += 1 
    end 
    end 
end 

我試圖確定的是:

  1. 這是計算這些隊伍最好的辦法還是有更多的資源有效的或乾的呢?
  2. 如果這是最好的方法,我該如何更改我的代碼並查看此@user的最高5個遊戲/ game_type排名,因爲代碼現在僅爲所有用戶計算排名?

回答

1

引入兩個新表來按遊戲類型存儲排名和排名。每小時計算一次總體排名。這樣頁面加載速度會更快。您可以使用像whenever這樣的寶石來安排每小時的排名計算。

您當前的實施不會超過幾百個用戶。

class User 
    has_many :points 
    has_many :game_ranks, :order => "rank DESC" 
    has_many :game_type_ranks, :order => "rank DESC" 

    # schedule this function every hour. 
    def self.update_rank 
    update_rank_by(Game) 
    update_rank_by(GameType) 
    end 

    def self.top_games(page_size=5) 
    game_ranks.includes(:game).limit(page_size) 
    end 

    def self.top_games_by_type(page_size=5) 
    game_type_ranks.includes(:game).limit(page_size) 
    end 

    def self.update_rank_by klass 
    rank_class = (klass.name + "Rank").constantize 
    rank_by_col = "#{klass.name.underscore}_id".to_sym   

    rank = total_points = rank_by = 0 
    page = 1;page_size=1000 
    sql = sum_points_by(rank_by_col) 
    while(points= sql.limit(page_size).offset((page-1)*page_size)).present? 
     page += 1 
     User.trasaction do 
     points.each do |point| 
      rank_by_col_val = point.send(rank_by_col) 

      # calculate rank 
      if (rank_by != rank_by_col_val) 
      rank = total_points = 0 
      rank_by = rank_by_col_val 
      end 

      if point.total_points > total_points 
      total_points = point.total_points 
      rank +=1 
      end 

      create_or_update_rank_object(rank_class, rank_by_col, point.user_id, rank_by_col_val, total_points, rank) 
     end 
     end  
    end  
    end 

    def self.sum_points_by(rank_by_col) 
    select_sql = "points.user_id, points.#{rank_by_col}, 
        SUM(points.points) total_points" 
    Point.select(select_sql).group(:user_id, rank_by_col). 
     order("#{rank_by_col}, total_points DESC") 
    end 

    def self.create_or_update_rank_object(rank_class, rank_by_col, user_id, rank_by_col_val, total_points, rank) 
    ro = rank_class.send(
     "find_or_initialize_by_user_id_and_#{rank_by_col}", 
     user_id, rank_by_col_val) 
    ro.total_points = total_points   
    ro.rank = rank 
    ro.save 
    end 
end 

添加一個新的模式,以保持隊伍,總積分爲每一位用戶和遊戲ID

class GameTypeRank 
    # Add columns total_points and rank 
    belongs_to :game_type 
    belongs_to :user 
end 

添加一個新的模式,以保持每個用戶和遊戲類型

class GameRank 
    # Add columns total_points and rank 
    belongs_to :game 
    belongs_to :user 
end 
隊伍和總積分

按用戶排名獲得排名前五位的遊戲

# array of game_id, game_name and game rank 
current_user.top_games.map {|r| [r.game.id, r.game.name, r.rank]} 
# array of game_type_id, game_type_name and game rank 
current_user.top_games_by_type.map { |r| 
    [r.game_type.id, r.game_type.name, r.rank] 
} 
+0

非常感謝哈里斯!我會試試這個,讓你知道! – yellowreign 2013-03-04 23:04:56

+0

嗨哈里什,我有幾個問題,只是因爲我想了解代碼在做什麼。排名如何計算?我無法弄清楚它是如何遞增的。此外,我想知道是否需要在GameRank.find_or_create之後添加.save(或者該語句是否也保存找到的記錄,如果存在的話)。謝謝! – yellowreign 2013-03-04 23:14:39

+0

我已經更新瞭解決更新問題的答案。我假設你正在使用Postgres數據庫。您可以在Postgres中使用Window函數來計算等級。理論上,您可以更新'Point'模型的'after_save'中的等級,但根據用戶數量計算等級的成本可能會過高。所以最好定期計算它。 – 2013-03-05 01:15:35