我正在尝试使用ActiveRecord编写一个相当复杂的查询(虽然我更愿意使用原始SQL,但是需要一些帮助来改进它。)
我有以下型号:
class Company < ActiveRecord::Base
has_many :contacts
has_many :workplace_stories
end
class WorkplaceStory < ActiveRecord::Base
belongs_to :company
has_many :questions
has_many :invites
end
class Invite < ActiveRecord::Base
belongs_to :workplace_story
belongs_to :contact
has_one :response
end
class Response < ActiveRecord::Base
belongs_to :invite
has_many :answers
end
class Contact < ActiveRecord::Base
belongs_to :company
has_many :invites
end
我想编写一个查询,该查询将选择所有没有任何属于给定联系人的邀请的工作场所故事,其中邀请有响应。换句话说,给出以下内容:
company = Company.last
=> #<Company:0x007fef5f58bd98>
contact = company.contact.last
=> #<Contact:0x007fef5f58bd98>
contact.invites.count
=> 1
contact.invites.first.response
=> #<Response:0x007fef5f58bd98>
company.workplace_stories.count
=> 5
company.workplace_stories.map { |x| x.invites.count }
=> [0, 0, 1, 2, 2]
我不想写一个查询,就像我下面的内容一样,它将返回所有工作场所的故事,其中没有任何带有响应的邀请(如果有的话)属于contact
。下面的查询接近我的目标,但它只选择至少有一个邀请的故事 - 在这种情况下,有两个。
stories = workplace_stories.joins(:invites).where.not(invites: {contact: contact}).order(updated_at: :desc).uniq.count
=> 2
答案 0 :(得分:0)
对于这样的查询,我希望首先在SQL中使用它,然后决定如何在ActiveRecord中表达它。看起来这是您正在寻找的查询:
SELECT *
FROM workplace_stories wps
WHERE NOT EXISTS (SELECT 1
FROM invites i
INNER JOIN responses r
ON r.invite_id = i.id
WHERE i.contact_id = ?
AND i.workplace_story_id = wps.id)
就个人而言,我喜欢写一个范围,这样可以很容易地重复使用,并且很容易与其他条件结合使用:
class WorkplaceStory
scope :without_response_by, -> (contact_id) {
where(<<-EOQ, contact_id)
NOT EXISTS (SELECT 1
FROM invites i
INNER JOIN responses r
ON r.invite_id = i.id
WHERE i.contact_id = ?
AND i.workplace_story_id = workplace_stories.id)
EOQ
}
# ...