2013-05-08 40 views
0

我有一个应用程序,用户可以收到游戏积分。我的代码中存在一个错误,这意味着在过去的一周内(在GameTypeRank表中)按Game_Type对每个用户的点数进行小计和排名。如果用户在过去一周内有积分,这可以正常工作,但如果用户在过去一周内没有积分,则无法正常工作。如果用户没有积分,那么他们应该在GameTypeRank中更新为0分,否则他们会保留最后的排名直到更新(并且排名不正确)。Rails:与Rails查询相关的逻辑错误

# user.rb: 
def self.update_game_type_weekly_rank 
    @game_types = GameType.all 
    @game_types.each do |game_type| 

    # this query is where the bug is since it is possible a User has no Points 
    @user_with_points = Point.where("game_type_id = ? and created_at >= ?", game_type.id, 1.week.ago).sum(:points, :group => :user_id, :order => 'sum(points) desc') 
    rank = point_counter = 0 

    @user_with_points.each do |user_id, points| 
     @game_type_rank = GameTypeRank.find_or_create_by_user_id_and_game_type_id(user_id, game_type.id) 
     if points != point_counter 
     point_counter = points 
     rank += 1 
     end 
     @game_type_rank.weekly_rank = rank 
     @game_type_rank.weekly_points = points 
     @game_type_rank.save 
    end 
    end 
end 

# Models 

# game_type_rank.rb 
# fields - user_id, game_type_id, weekly_points, weekly_rank 
belongs_to :game_type 
belongs_to :user 

# point.rb 
# fields - user_id, points, game_type_id 
belongs_to :game 
belongs_to :game_type 
belongs_to :user 

我可以创建每周运行,并创建点记录(含Point.points = 0),对于没有任何一周的每个用户,但是这是一个贫穷的解决方案的方法。

我也可能会在方法开始时为每个GameTypeRank记录初始化weekly_pointsweekly_rank,但对我来说这似乎效率低下(我不确定是否执行此操作的最佳方式)。

# Sample Data 
Points 
|user_id|points|game_type_id|created_at 
|1  | 10 |   1|2013-05-07 
|1  | 10 |   2|2013-05-07 
|2  | 20 |   2|2012-12-31 
|1  | 5 |   2|2012-12-31 

before weekly_update - Game_Type_Ranks 
|user_id |game_type_id | weekly_points | weekly_rank| 
|1  |2   |5    |2 
|2  |2   |20    |1 

after weekly_update - Game_Type_Ranks 
|user_id |game_type_id | weekly_points | weekly_rank| 
|1  |1   |10    |1 
|1  |2   |10    |1 
|2  |2   |20    |1 

what should happen after weekly_update - Game_Type_Ranks 
|user_id |game_type_id | weekly_points | weekly_rank| 
|1  |1   |10    |1 
|1  |2   |10    |1 
|2  |2   |0    |2 <== update for user_id 2 
+0

一些示例输入数据以及预期和实际输出将会很有用。 – 2013-05-08 00:21:02

+0

我添加了一些示例数据,谢谢 – yellowreign 2013-05-08 00:33:59

+0

本周没有活动的用户是否需要每周有0个点。我们可以将它们排除在外吗? – bennick 2013-05-08 16:25:12

回答

0

的主要问题是你没有访问谁拥有现有的排名,但并没有积极为当前周@user_with_points对于给定的GameType用户。

为什么不保留原样,而是使用updated_at时间戳。在打电话给GameTypeRank对象时,只需拉取上周更新的对象即可。

# some controller 
GameTypeRank.where("id = ? and updated_at >= ?", id, 1.week.ago) 

但是这个你失去了把旧的能力倾斜用户weekly_points 0

另一种方法是通过循环具有对给定的游戏类型分不分时间,像这样所有用户:

# user.rb: 
def self.update_game_type_weekly_rank 
    @game_types = GameType.all 
    @game_types.each do |game_type| 

    @user_with_points = Point.where("game_type_id = ?", game_type.id).sum(:points, :group => :user_id, :order => 'sum(points) desc') 
     rank = point_counter = 0 
     @user_with_points.each do |user_id, points| 
     @game_type_rank = GameTypeRank.find_or_create_by_user_id_and_game_type_id(user_id, game_type.id) 
     if points != point_counter 
      point_counter = points 
      rank += 1 
     end 
     @game_type_rank.weekly_rank = rank 
     @game_type_rank.weekly_points = points 
     @game_type_rank.save 
     end 
    end 

    end 
end 

之后,你的表应该是你想要的方式。现在,您可能会意识到这一点是有代价的。也许你可能会因为不活动而被切断。比如说6个月。

# users.rb 
.... 
@user_with_points = Point.where("game_type_id = ? and created_at >= ?", game_type.id, 6.months.ago).sum(:points, :group => :user_id, :order => 'sum(points) desc') 
.... 

# some controller 
GameTypeRank.where("id = ? and created_at >= ?", id, 6.months.ago) 

这两种方法都有一些低效率,但它完成了工作。另外,您可能正在使用cron作业和/或后台作业运行此方法,因此对用户的影响应为零。

+0

谢谢你的回答。在第一个选项中,正如你所指出的那样,它不会纠正GameTypeRank在前一周有用户点数的情况,但不是这一点。我认为第二种选择的问题在于,即使他们本周没有积分,它也会对每个用户进行排名。 GameTypeRank实际上有一个排名,weekly_rank和monthly_rank(和相应的点字段),所以我已经有了选项2的代码(省略created_at),对于我的总体排名,点字段,但不是每月或每周。 – yellowreign 2013-05-08 20:59:58