I have two queries, I need an or
between them, i.e. I want results that are returned by either the first or the second query.
First query is a simple where()
which gets all available items.
@items = @items.where(available: true)
Second includes a join()
and gives the current user's items.
@items =
@items
.joins(:orders)
.where(orders: { user_id: current_user.id})
I tried to combine these with Rails' or()
method in various forms, including:
@items =
@items
.joins(:orders)
.where(orders: { user_id: current_user.id})
.or(
@items
.joins(:orders)
.where(available: true)
)
But I keep running into this error and I'm not sure how to fix it.
Relation passed to #or must be structurally compatible. Incompatible values: [:references]
There is a known issue about it on Github.
According to this comment you might want to override the structurally_incompatible_values_for_or
to overcome the issue:
def structurally_incompatible_values_for_or(other)
Relation::SINGLE_VALUE_METHODS.reject { |m| send("#{m}_value") == other.send("#{m}_value") } +
(Relation::MULTI_VALUE_METHODS - [:eager_load, :references, :extending]).reject { |m| send("#{m}_values") == other.send("#{m}_values") } +
(Relation::CLAUSE_METHODS - [:having, :where]).reject { |m| send("#{m}_clause") == other.send("#{m}_clause") }
end
Also there is always an option to use SQL:
@items
.joins(:orders)
.where("orders.user_id = ? OR items.available = true", current_user.id)
You can write the query in this good old way to avoid error
@items = @items.joins(:orders).where("items.available = ? OR orders.user_id = ?", true, current_user.id)
Hope that helps!
Hacky workaround: do all your .joins
after the .or
. This hides the offending .joins
from the checker. That is, convert the code in the original question to...
@items =
@items
.where(orders: { user_id: current_user.id})
.or(
@items
.where(available: true)
)
.joins(:orders) # sneaky, but works! 😈
More generally, the following two lines will both fail
A.joins(:b).where(bs: b_query).or(A.where(query)) # error! 😞
A.where(query).or(A.joins(:b).where(bs: b_query)) # error! 😞
but rearrange as follows, and you can evade the checker:
A.where(query).or(A.where(bs: b_query)).joins(:b) # works 😈
This works because all the checking happens inside the .or()
method. It's blissfully unaware of shennanigans on its downstream results.
One downside of course is it doesn't read as nicely.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With