DatingApp编程:ActiveRecord关联,用于查找没有批准或单向批准的用户

qua*_*ato 9 postgresql activerecord ruby-on-rails

我正在建立一个约会风格的应用程序,其中Users可以批准其他用户.我使用Approval模型来跟踪这些关系.每个Approval都有一个user_id和一个approved_id- 批准的用户ID User.它还有rejected_at一个日期时间,表示一个User人拒绝了另一个人.

要向符合条件的用户提供current_user,我必须查询其中任何一个的用户

  • 没有Approval关系
  • Approval关系只与approved_idas current_user.id(意味着符合条件的用户批准current_user但反过来没有任何关系)
  • 排除User具有Approval非nil rejected_at属性的s,其中approved_idUser或者user_idcurrent_user.

如何创建ActiveRecord查询以查找符合条件的用户?我知道我可以做joinsApproval,但我也想考虑那里是没有Approval关系的User小号!我认为只做两个单独的查询可能更有意义,但我想知道是否可以组合成一个...

Kri*_*ján 6

您想要的行为是LEFT OUTER JOIN,它将包括来自users是否存在任何匹配行的行approvals.这样,你得到一个User没有发表Approval关于你的目标的人User,或者一个已经发布并且我们可以过滤拒绝的人.

查询看起来像

-- Looking for users who either have no opinion or have approved user 1
SELECT *
FROM users
LEFT OUTER JOIN approvals
  ON users.id = approvals.user_id
  AND approvals.approved_id = 1 -- Filter for only approvals of user 1
WHERE users.id != 1 -- Ignore user 1
  AND (
    approvals.approved_id IS NULL -- Approving user has no opinion of user 1
    OR approvals.rejected_at IS NULL -- Approving user has not rejected user 1
  )
;
Run Code Online (Sandbox Code Playgroud)

碎片,

  • users LEFT OUTER JOIN approvals考虑所有users,即使他们没有approvals
  • ON users.id = approvals.user_idusersapprovals他们创造的对
  • ON ... AND approvals.approved_id = 1只考虑approvalsUser 1
  • WHERE users.id != 1 只考虑其他 users
  • WHERE ... approvals.approved_id IS NULL采用users的是没有创造任何approvals有关User 1.
  • WHERE ... approvals.rejected_at IS NULL需要users的是创建ApprovalUser 1,这是不是一个拒绝

ActiveRecord 当我们翻译它时,它没有做任何特别漂亮的事情,但它有效:

class User < ActiveRecord::Base
  def eligible_partners
    User.joins( # Join like we did before
      <<-SQL
        LEFT OUTER JOIN approvals
        ON  users.id = approvals.user_id
        AND approvals.approved_id = #{self.id}
      SQL
    ).where.not(id: id) # Ignore ourselves
     .where( # Filter for no approvals or a positive approval
       <<-SQL
         approvals.approved_id IS NULL
         OR approvals.rejected_at IS NULL
       SQL
     )
  end
end
Run Code Online (Sandbox Code Playgroud)

如果你想要更具可读性的东西,你可以收集每个User拒绝给定人的ID ,然后获得所有其他User的ID .一方面,这是两个问题; 另一方面,它可能比JOIN你的桌子变大后便宜,而且它更容易理解.

  def other_eligible_partners
    rejecter_ids = Approval
      .where(approved_id: id) # About ourselves
      .where.not(rejected_at: nil) # Rejected us
      .pluck(:user_id)
    User.where.not(id: rejecter_ids + [id]) # People who didn't reject us
  end
Run Code Online (Sandbox Code Playgroud)