带有活动记录的复杂查询

Complicated query with active record

我一整天都在试图弄清楚这个查询,但我不知道如何得到它。

我有模型Book

class Book < ActiveRecord::Base
    # Relationships
    has_many :requests, dependent: :destroy
end

和这个模型Request

class Request < ActiveRecord::Base
    belongs_to :book

    # Enumerables
    enum status: [:pending, :accepted, :completed]
end

我想查询以获取以下书籍:

  1. 没有任何要求
  2. 确实有请求,但其中 none 的状态为 :completed

我的2美分:

我设法把它们分开了:

scope :without_requests, -> {
    requested_books_ids = Request.pluck(:book_id)
    where.not(id: requested_books_ids)
}

scope :with_requests_but_not_completed, -> {
    includes(:requests).where.not(requests: {status: Request.statuses[:completed] })
}

但是我没有成功地将结果合并在一起。我试过这个:

@books = Book.without_requests + Book.with_requests_but_not_completed
@books = @books.paginate(page: params[:page], per_page: 2)

但这失败了 the will_paginate gem

有什么建议吗?

更新

不确定是否相关...但我使用 SQLite 进行开发和测试,使用 Postgresql 进行生产。我听说最好在所有系统上使用相同的系统,但这不应该改变查询的结果,不是吗?

这可以使用原始 SQL 轻松完成,但您似乎想要依赖 ActiveRecord 提供的 DSL。

您需要OR,遗憾的是没有。
最好的办法是自己实施,例如使用此方法:

def join_with_or(*relations)
  relations.map do |relation|
    clause = relation.arel.where_sql.sub(%r{\AWHERE }, '')
    "(#{clause})"
  end.join(' OR ')
end


one = Book.where.not(id: Request.pluck(:book_id))
two = Book.joins(:requests).where.not(requests: { status: Request.statuses[:completed] })

Book.where(join_with_or(one, two))

你的方法不错。如果你想坚持下去,以下是什么:

Book.all
    .eager_load(:requests)
    .where([
      "books.id not in (?) or requests.status <> ?",
      Request.pluck(:book_id),
      Request.statuses[:completed]
    ])

eager_load 基本上是您应该使用的 includes(:requests) + references(:status)。在这种情况下你想要加入所以我更喜欢使用预加载,你可以查看这篇文章,它很好地解释了 what eager_load is