【问题标题】:Rails: Finding records where a nested association is emptyRails:查找嵌套关联为空的记录
【发布时间】:2021-11-26 03:51:59
【问题描述】:

在我正在开发的 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 个时事通讯。
  • 每个小组成员都可以为给定的时事通讯创作多个​​故事。
  • 每个故事都是以下状态之一:草稿已提交已发布
  • 故事草稿可能与时事通讯关联,也可能不关联
  • 提交或发布的故事与时事通讯相关联

我想找出给定时事通讯的哪些作者没有处于草稿已提交状态的故事。

鉴于newsletter_id,我可以通过这样的查询找出确实有草稿或提交故事的成员:

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

但是,我不确定如何否定这一点并获得与那组作者相反的观点。也就是说,给定时事通讯的作者没有草稿或提交的故事。他们是否发表了故事应该没有区别。

编辑

几个月前,我向a similar question 询问了如何识别不存在关联模型记录的记录。我认为这是我需要在这里做的非常相似的方法,但是由于GroupMember (as Newsletter::Author) -> Newsletter -> Newsletter::Story

这里的纯 SQL 答案也会很有启发性。

【问题讨论】:

    标签: sql ruby-on-rails ruby activerecord


    【解决方案1】:

    你离得太近了。你只需要添加not

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

    【讨论】:

    • 谢谢@Sara,这就是我所怀疑的,但这并不像我预期的那样有效。如果我查看该查询的结果,我仍然会看到带有submitted 故事的作者。我认为我遇到的部分是我想找到可能根本没有故事(或只是发表故事)的作者。这意味着我需要根据提交和草稿故事不存在的情况进行过滤,而且我认为我不能使用简单的 WHERE NOT 子句来做到这一点。
    【解决方案2】:

    深入了解一些 Arel 语句,我能够通过使用 NOT EXISTS 子句和 JOINing 向 GroupMembers 发送时事通讯故事,过滤不具有 @987654323 状态的故事,从而实现我所需要的。 @ 或 submitted。鉴于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')
          )
        )
      )
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多