Rails:查找嵌套关联为空的记录



在我正在开发的一个Rails应用程序中,我有几个不同的模型关联在一起,像这样(为了清晰起见,简化了):

group.rb

class Group < ApplicationRecord
has_many :members, class_name: 'GroupMember'
has_many :newsletters
end

group_member.rb

class GroupMember < ApplicationRecord
belongs_to :group
has_many :authorships, inverse_of: :group_member, class_name: "Newsletter::Author"
has_many :stories, inverse_of: :author, class_name: "Newsletter::Story"
end

newsletter.rb

class Newsletter < ApplicationRecord
has_many :authors, inverse_of: :newsletter
has_many :stories
end

newsletter/author.rb

class Newsletter::Author < ApplicationRecord
belongs_to :newsletter, inverse_of: :authors
belongs_to :group_member, class_name: "GroupMember", inverse_of: :authorships
end

newsletter/story.rb

class Newsletter::Story < ApplicationRecord
belongs_to :newsletter, inverse_of: :stories, optional: true
belongs_to :author, inverse_of: :stories, class_name: "GroupMember"
enum status: {draft: "draft", submitted: "submitted", published: "published"}, _default: "draft"
end

给定上述相关模型,以下是我正在工作的框架:

  • 每个通讯有n作者(组成员)和n通讯。
  • 每个小组成员可以为给定的通讯作者多个故事。
  • 每个故事是这些状态状态之一:草稿,,或者published
  • 新闻报道草稿可以与时事通讯关联,也可以不关联
  • 提交或发布的故事与新闻通讯相关联

我想知道给定时事通讯的哪些作者没有草稿的故事提交或地位。

给定newsletter_id,我可以找到有草稿或提交故事的成员,查询如下:

Newsletter.find(newsletter_id).authors
.joins(group_member: :stories)
.where(stories: {status: [:draft, :submitted]})
.distinct

然而,我不确定如何否定这一点,并得到相反的一组作者。,对于一个给定的通讯作者没有草案或提交的故事。他们是否发表了故事应该没有区别。

编辑

几个月前我问过一个类似的问题,关于识别关联模型的记录不存在的记录。我认为这是一个非常类似的方法,我需要在这里做的,但我还没有完全破解如何将这个答案应用到这个问题,由于嵌套的关联GroupMember (as Newsletter::Author)->NewsletterNewsletter::Story

一个纯SQL的答案也会很有启发性。

你很接近了。你只需要添加not

Newsletter.find(newsletter_id).authors
.joins(group_member: :stories)
.where.not(stories: {status: [:draft, :submitted]})
.distinct

在一些Arel语句中,我能够通过使用NOT EXISTS子句以及JOIN将Newsletter Stories发送给GroupMembers,过滤没有draftsubmitted状态的故事来实现我在这里需要的功能。考虑到newsletter_id,以下是对我有效的方法:

Newsletter::Author
.where(newsletter_id: newsletter_id)
.where(
GroupMember.select("1")
.where(
Newsletter::Story.arel_table[:author_id].eq(Newsletter::Author.arel_table[:group_member_id])
)
.joins(:stories)
.where.not(
Newsletter::Story.arel_table[:status].not_eq_all([:draft, :submitted])
)
.arel.exists.not
)

生成的SQL看起来像这样:

SELECT * FROM newsletter_authors
WHERE newsletter_authors.newsletter_id = [newsletter_id]
AND NOT (
EXISTS (
SELECT 1 FROM group_members
INNER JOIN newsletter_stories ON newsletter_stories.author_id = group_members.id
WHERE newsletter_stories.author_id = newsletter_authors.group_member_id
AND NOT (
(newsletter_stories.status != 'draft' AND newsletter_stories.status != 'submitted')
)
)
)

最新更新