如何在关联列上执行where子句?

时间:2015-12-01 01:04:43

标签: ruby-on-rails ruby-on-rails-4 activerecord

我有两个模型:ConnectionMembership ...它们之间有以下关联:

class Membership < ActiveRecord::Base
  belongs_to :inviter, class_name: "User", foreign_key: "user_id"
  belongs_to :invited, class_name: "User", foreign_key: "invited_id"
  has_many :connections, dependent: :destroy
end

class Connection < ActiveRecord::Base
  belongs_to :inviter_membership, class_name: "Membership", foreign_key: "membership_id"
  belongs_to :invited_membership, class_name: "Membership", foreign_key: "invited_membership_id"    
end

以下是两个membership个对象:

[34] pry(#<Membership>)> self
=> #<Membership id: 54, family_tree_id: 43, user_id: 41, created_at: "2015-12-01 00:40:37", updated_at: "2015-12-01 00:40:37", relation: "wife", member_id: nil, invited_id: 1, relative_type: 0>
[35] pry(#<Membership>)> mem
=> #<Membership id: 53, family_tree_id: 1, user_id: 1, created_at: "2015-12-01 00:39:58", updated_at: "2015-12-01 00:40:37", relation: "husband", member_id: nil, invited_id: 41, relative_type: 0>
[39] pry(#<Membership>)> self.invited_id
=> 1

然后我有一个connection对象:

[36] pry(#<Membership>)> c
=> #<Connection id: 38, membership_id: 53, sent_at: "2015-12-01 00:40:14", responded_at: nil, send_limit: nil, times_sent: 1, removed_at: nil, created_at: "2015-12-01 00:40:14", updated_at: "2015-12-01 00:40:14", request_status: 0, invited_membership_id: nil>

如何找到connection(或某些值)的所有membership.invited_id = 41条记录?

换句话说,我想做这样的事情:

Connection.where("membership.invited_id = ? OR invited_membership.invited_id = ?", self.id, self.id).exists?

但是当我尝试上述操作时,我收到了这个错误:

[138] pry(main)> Connection.where("membership.invited_id = ? OR invited_membership.invited_id = ?", 41, 41).exists?
  Connection Exists (165.3ms)  SELECT  1 AS one FROM "connections"  WHERE (membership.invited_id = 41 OR invited_membership.invited_id = 41) LIMIT 1
PG::UndefinedTable: ERROR:  missing FROM-clause entry for table "membership"
LINE 1: SELECT  1 AS one FROM "connections"  WHERE (membership.invit...

那么如何运行where查询以将相关记录的列与我提供的参数相匹配?

修改1

基于@messanjah的精彩建议,我几乎就在那里。这是我正在使用的查询:

Connection.joins(:inviter_membership, :invited_membership).
      where("memberships.invited_id = ? OR invited_memberships_connections.invited_id = ? OR memberships.user_id = ? OR invited_memberships_connections.user_id = ?", self.inviter.try(:id), self.inviter.try(:id), self.invited.try(:id), self.invited.try(:id)).exists?

self是这样的:

[22] pry(#<Membership>)> self
=> #<Membership id: 69, family_tree_id: 49, user_id: 47, created_at: "2015-12-01 04:53:17", updated_at: "2015-12-01 04:53:17", relation: "wife", member_id: nil, invited_id: 1, relative_type: 0>

但是当我运行该查询时,它会返回一个空数组或false

[14] pry(#<Membership>)> connection_exists
  CACHE (0.0ms)  SELECT  1 AS one FROM "connections" INNER JOIN "memberships" ON "memberships"."id" = "connections"."membership_id" INNER JOIN "memberships" "invited_memberships_connections" ON "invited_memberships_connections"."id" = "connections"."invited_membership_id" WHERE (memberships.invited_id = 47 OR invited_memberships_connections.invited_id = 47 OR memberships.user_id = 1 OR invited_memberships_connections.user_id = 1) LIMIT 1
=> false

尽管有这些ID的memberships记录,但可以在这里看到:

[24] pry(#<Membership>)> c
=> #<Connection id: 49, membership_id: 68, sent_at: "2015-12-01 04:52:51", responded_at: nil, send_limit: nil, times_sent: 1, removed_at: nil, created_at: "2015-12-01 04:52:51", updated_at: "2015-12-01 04:52:51", request_status: 0, invited_membership_id: nil>
[25] pry(#<Membership>)> c.inviter_membership
=> #<Membership id: 68, family_tree_id: 1, user_id: 1, created_at: "2015-12-01 04:52:32", updated_at: "2015-12-01 04:53:17", relation: "husband", member_id: nil, invited_id: 47, relative_type: 0>
[26] pry(#<Membership>)> c.invited_membership
=> nil
[27] pry(#<Membership>)> c.inviter_membership.invited_id
=> 47
[28] pry(#<Membership>)> c.inviter_membership.user_id
=> 1

请注意最后两个查询中返回的IDs,但在传入主查询时,我将其返回为空。

导致这种情况的原因是什么?

修改2

这是我正在尝试的新代码:

Connection.joins("LEFT JOIN memberships inviter_memberships ON inviter_memberships.id = connections.membership_id").
  joins("LEFT JOIN memberships invited_memberships ON invited_memberships.id = connections.invited_membership_id").
  where("inviter_memberships.invited_id = ? OR invited_memberships.invited_id = ?
  OR inviter_memberships.user_id = ? OR invited_memberships.user_id = ?", self.invited.try(:id), self.invited.try(:id), self.inviter.try(:id), self.inviter.try(:id)).exists?

self的位置:

[15] pry(#<Membership>)> self
=> #<Membership id: 71, family_tree_id: 50, user_id: 48, created_at: "2015-12-01 05:46:00", updated_at: "2015-12-01 05:46:00", relation: "wife", member_id: nil, invited_id: 1, relative_type: 0>
[20] pry(#<Membership>)> self.invited
=> #<User id: 1, email: "abc@test.com", encrypted_password: "$2a$10$IQXTVqZmMaUUv4WZlwXze.OTfWki2d2qZEjj01isCZZ...", reset_password_token: nil, reset_password_sent_at: nil, remember_created_at: nil, sign_in_count: 7, current_sign_in_at: "2015-09-25 09:35:43", last_sign_in_at: "2015-09-25 01:36:10", current_sign_in_ip: "127.0.0.1", last_sign_in_ip: "192.168.1.106", created_at: "2015-07-25 02:20:15", updated_at: "2015-09-25 09:35:43", first_name: "Marc", confirmation_token: nil, confirmed_at: "2015-07-25 02:20:16", confirmation_sent_at: "2015-07-25 02:20:16", unconfirmed_email: nil, invitation_relation: nil, avatar: "Me-Bio-Pic.jpg", invitation_token: nil, invitation_created_at: nil, invitation_sent_at: nil, invitation_accepted_at: nil, invitation_limit: nil, invited_by_id: nil, invited_by_type: nil, invitations_count: 0, bio: "I am Marc Gayle. I build things and I love my fami...", last_name: "Gayle", gender: 0, birthday: nil>
[21] pry(#<Membership>)> self.inviter
=> #<User id: 48, email: "def@test.com", encrypted_password: "", reset_password_token: nil, reset_password_sent_at: nil, remember_created_at: nil, sign_in_count: 0, current_sign_in_at: nil, last_sign_in_at: nil, current_sign_in_ip: nil, last_sign_in_ip: nil, created_at: "2015-12-01 05:40:45", updated_at: "2015-12-01 05:40:45", first_name: "Cheyenne-Kari", confirmation_token: nil, confirmed_at: nil, confirmation_sent_at: nil, unconfirmed_email: nil, invitation_relation: "husband", avatar: nil, invitation_token: "D8uPGyxAeX95kfYGxRq-", invitation_created_at: "2015-12-01 05:40:45", invitation_sent_at: "2015-12-01 05:40:45", invitation_accepted_at: nil, invitation_limit: nil, invited_by_id: 1, invited_by_type: "User", invitations_count: 0, bio: nil, last_name: "Gayle", gender: 1, birthday: nil>

基本上我要做的是尝试发现我即将创建的相关connection记录是否存在membership记录(为了不创建重复connection记录)。

换句话说,每个connection记录应与inviter_membershipinvited_membership相对应。

现在,正在after_create :setup_connection模型的Membership回调上创建连接。因此,我尝试检查数据库中是否存在具有相关Connection模型的membership记录,然后只更新connection记录,而不是创建新记录。

这就是为什么我的代码片段中的self是一个membership记录(因为我在运行时使用pry进行调试 - 即在after_create之后在创建新的connection记录之前调用。

我希望这有助于澄清我想要做的事情。如果没有,我可以解释一下。

编辑3

以下是执行上述LEFT JOIN SQL的服务器日志:

[1] pry(#<Membership>)> connection_exists
  CACHE (0.0ms)  SELECT  1 AS one FROM "connections" LEFT JOIN memberships inviter_memberships ON inviter_memberships.id = connections.membership_id LEFT JOIN memberships invited_memberships ON invited_memberships.id = connections.invited_membership_id WHERE (inviter_memberships.invited_id = 1 OR invited_memberships.invited_id = 1
      OR inviter_memberships.user_id = 51 OR invited_memberships.user_id = 51) LIMIT 1
=> false

1 个答案:

答案 0 :(得分:2)

您可以加入关联,将其列添加到where

Connection.joins(:inviter_membership, :invited_membership).
  where("memberships.invited_id = ? OR invited_memberships_connections.invited_id = ?", self.id, self.id)

这有点尴尬,因为表别名不明显(我使用的是Rails 4.2.3,YMMV)。

您可能希望明确构造联接,以便更好地控制查询:

Connection.
  joins("INNER JOIN memberships inviter_memberships ON inviter_memberships.id = connections.inviter_membership_id").
  joins("INNER JOIN memberships invited_memberships ON invited_memberships.id = connections.invited_membership_id").
  where("inviter_memberships.invited_id = ?
      OR invited_memberships.invited_id = ?", self.id, self.id)

如果关联记录可能不存在(例如,Connection可能没有invite_membership),请使用LEFT联接以避免将其丢弃。

Connection.
  joins("LEFT JOIN memberships inviter_memberships ON inviter_memberships.id = connections.inviter_membership_id").
  joins("LEFT JOIN memberships invited_memberships ON invited_memberships.id = connections.invited_membership_id").
  where("inviter_memberships.invited_id = ?
      OR invited_memberships.invited_id = ?", self.id, self.id)