带有活动记录的复杂查询

时间:2015-07-29 23:31:07

标签: ruby-on-rails ruby-on-rails-4 activerecord rails-activerecord will-paginate

我一直试图弄清楚这个查询,我不知道如何得到它。

我有一个模型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. 有请求,但没有一个请求状态为:completed
  3. 我的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进行生产。我听说最好有相同的系统,但这不应该改变查询的结果,不是吗?

2 个答案:

答案 0 :(得分:1)

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

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

答案 1 :(得分:0)

这可以通过原始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))