带有活动记录的复杂查询
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
我想查询以获取以下书籍:
- 没有任何要求
- 确实有请求,但其中 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
我一整天都在试图弄清楚这个查询,但我不知道如何得到它。
我有模型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
我想查询以获取以下书籍:
- 没有任何要求
- 确实有请求,但其中 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