2013-03-04 59 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