我有一个应用程序,用户收到游戏积分。我的代码中有一个bug,它的目的是根据过去一周的Game_Type对每个用户的点数进行小计和排名(在GameTypeRank表中)。如果用户在过去的一周内有积分,这可以很好地工作,但是,如果用户在过去一周没有积分,它就不能正常工作。如果用户没有积分,他们应该在GameTypeRank中更新为0分,否则,他们将保留其最后的排名,直到更新(排名是不正确的)。
# 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_points和weekly_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发布于 2013-05-09 01:02:12
主要问题是,对于给定的GameType,您无法访问具有现有排名但在@user_with_points中的当前一周未处于活动状态的用户。
为什么不保留原样,而使用updated_at时间戳呢?在调用您的GameTypeRank对象时,只需提取上周更新的对象即可。
# some controller
GameTypeRank.where("id = ? and updated_at >= ?", id, 1.week.ago)但是,这样您就失去了将旧的raked用户的weekly_points设置为0的能力。
另一种方法是循环遍历所有具有给定GameType的点数的用户,而不考虑时间,如下所示:
# 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作业和/或后台作业运行此方法,因此对用户的影响应该为零。
https://stackoverflow.com/questions/16430678
复制相似问题