Rails 3:给定一名球员,找出他们与每个对手配对的次数

Rails 3: Given a player, find the number of times they have been paired with each opponent

我正在使用 Rails 3.2。我的模式相当复杂但很简单,我有个人电脑(代表玩家)、游戏和项目,因此:

class Game < ActiveRecord::Base
   has_many :projects

class Pc < ActiveRecord::Base
   has_many :projects

class Project < ActiveRecord::Base
   has_many :games
   belongs_to :pc

注意:GameProject 还有一个 table 以及多对多关联。

我试图找到一个查询(ActiveRecord 优于完全原始的 SQL)这样,给定一台 Pc,returns 是其他每台 Pc 玩过的游戏数量反对他们。如果同一游戏中有两个 Pc 的项目,则一个 Pc 与另一个 Pc 对战。示例情况是:

+------+---------+----+
| Game | Project | Pc |
+------+---------+----+
|    1 |       1 |  1 |
|    1 |       2 |  1 |
|    1 |       3 |  1 |
|    1 |       4 |  2 |
|    1 |       5 |  2 |
|    2 |       6 |  1 |
|    2 |       7 |  2 |
|    3 |       8 |  1 |
|    3 |       9 |  3 |
+------+---------+----+

如果我想知道每个对手和Pc1交过多少次,结果应该是:

{2 => 2, 3 => 1}

因为 Pc2 和 Pc1 打过两场比赛,而 Pc3 只和 Pc1 打过一次。 我不在乎Pc1本身是否出现在结果中,我只是在之后不处理它。

对 Pc2 的相同查询应该 return:

{1 => 2, 3 => 0}

因为Pc1和Pc1玩过两次,而Pc3没有和Pc2玩过任何游戏。

我已经尝试了一段时间,但我似乎做不好。

提前致谢!

编辑 1

我想我找到了一个原始的 sql 查询来完成这项工作(仍然需要测试它)。看了一下,好像不能用ActiveRecord的查询方式来完成。查询如下:

sql = "
SELECT 
  pc_id, 
  COUNT(DISTINCT(game_id)) 
FROM 
  (SELECT 
    g.id as game_id,  
    p.pc_id as pc_id 
  FROM games g
  INNER JOIN game_projects gp ON gp.game_id = g.id 
  INNER JOIN projects p ON p.id = gp.project_id
  WHERE g.id IN (SELECT g2.id FROM games g2 JOIN game_projects gp2 ON gp2.game_id = g2.id JOIN projects p2 ON p2.id = gp2.project_id AND p2.pc_id = #{pc.id}) 
  ) AS t 
GROUP BY pc_id;"

ActiveRecord::Base.connection.execute(sql).to_a

编辑 2

我似乎找到了将查询和子查询一分为二的解决方案:

game_ids = Game.joins(:projects).where('projects.pc_id = ?', pc.id).map(&:id)
Pc.joins(:projects => :games).where('games.id IN (?)', game_ids).group('pcs.id').count

甚至更简单,第二个是:

Project.joins(:games).where('games.id IN (?)', game_ids).group('projects.pc_id').count

编辑 3

I don't care if Pc1 itself appears in the result, I'll just not process it afterwards.

为避免这种情况,我们可以将 AND projects.pc_id != ? 添加到 WHERE 条件中。

我最终将查询一分为二,这样就简单多了。

game_ids = Game.joins(:projects).where('projects.pc_id = ?', pc.id).map(&:id)
Project.joins(:games).where('games.id IN (?) AND projects.pc_id != ?', game_ids, pc.id).group('projects.pc_id').count