Yutaro Tanaka
Yutaro Tanaka

Reputation: 1

Rails5: How can I use the ActiveRecord "OR" query with "includes"?

class Parent < ApplicationRecord
  has_many :children

  enum status: {
     status1: 0,
     status2: 1     
  }
end

class Child < ApplicationRecord
  belongs_to :parent
end

# error
# "Relation passed to #or must be structurally compatible. Incompatible values: [:references]"
combination = Parent.status1.or(Parent.status2.includes(:children).where(children: {name: 'ABC'}))

I want to get the data "status1" or "status2 has children named 'ABC'", but error occurs.

Upvotes: 0

Views: 2629

Answers (2)

Robin Daugherty
Robin Daugherty

Reputation: 7524

The or method takes another relation that has a similar filter pattern, and combines it with the already-existing filters on the object being called.

For example, Parent.status1.or(Parent.status2) would give you a set of records that have either status: 1 or status: 2.

(In case someone is not familiar with it, the example in the question also uses enum, which allows filtering the enum's attribute value using the name of the value. #status1 and #status2 in this case correspond to { status: 0 } and {status: 1} respectively.)

In order to call more relation methods to modify the final result, you must call them on the result of calling #or, like this:

Parent.status1.or(Parent.status2).includes(:children).where(children: {name: 'ABC'})

Based on your comment I see now that you want records that either (have status1) or (have status2 and have a matching children record).

Note that in order to use a relation in a where (like where(children: { name: value }) you must join with the related table (joins(:children).where(children: { name: value }). It seems that ActiveRecord will infer the join if you use only includes, but that's not documented as far as I can tell. This is why or sees the two relations as incompatible: one has children in the references list, while the other does not.

If you write the where clause by hand as a string, it does not change the references list, so or does not see the relation as incompatible. When you write a where clause by hand, you must explicitly use joins:

Parent.status1.joins(:children).or(Parent.status2.joins(:children).where("children.name = 'ABC'"))

Upvotes: 2

Sapna Jindal
Sapna Jindal

Reputation: 422

You are not calling "includes" on the final or result.

parent = Parent.status1.or(Parent.status2)
parent.includes(:chilren).where(children: {name: "ABC"})

Upvotes: 0

Related Questions