Отношение, переданное в #or, должно быть структурно совместимым. Несовместимые значения: [:ссылки]

У меня есть два запроса, мне нужно or между ними, т.е. я хочу, чтобы результаты возвращались либо первым, либо вторым запросом.

Первый запрос — это простой where(), который получает все доступные элементы.

@items = @items.where(available: true)

Второй включает join() и дает элементы текущего пользователя.

@items =
  @items
  .joins(:orders)
  .where(orders: { user_id: current_user.id})

Я попытался объединить их с методом or() Rails в различных формах, в том числе:

@items =
  @items
  .joins(:orders)
  .where(orders: { user_id: current_user.id})
  .or(
    @items
    .joins(:orders)
    .where(available: true)
  )

Но я продолжаю сталкиваться с этой ошибкой и не знаю, как ее исправить.

Relation passed to #or must be structurally compatible. Incompatible values: [:references]

person frostbite    schedule 22.11.2016    source источник


Ответы (3)


На Github есть известная проблема.

Согласно этому комментарию вы можете переопределить structurally_incompatible_values_for_or, чтобы решить проблему:

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

Также всегда есть возможность использовать SQL:

@items
  .joins(:orders)
  .where("orders.user_id = ? OR items.available = true", current_user.id)
person Andrey Deineko    schedule 22.11.2016
comment
Вместо этого пошли с SQL-запросами. Спасибо за информацию. - person frostbite; 22.11.2016
comment
@Andrey Андрей должен был сослаться на мой ответ вместо того, чтобы редактировать свой и добавлять то же самое. В любом случае +1 за первое решение. - person Rajdeep Singh; 22.11.2016
comment
@RSB Я собирался начать с части sql, затем погуглил сообщение об ошибке и нашел проблему github. В конце концов, я все же решил добавить свою первоначальную мысль, так как это меньше кода, чтобы заставить его работать. - person Andrey Deineko; 22.11.2016

Вы можете написать запрос таким старым добрым способом, чтобы избежать ошибок

@items = @items.joins(:orders).where("items.available = ? OR orders.user_id = ?", true, current_user.id)

Надеюсь, это поможет!

person Rajdeep Singh    schedule 22.11.2016

Хакерский обходной путь: делайте все свои .joins после .or. Это скрывает оскорбительный .joins от средства проверки. То есть преобразовать код исходного вопроса в...

@items =
  @items
  .where(orders: { user_id: current_user.id})
  .or(
    @items
    .where(available: true)
  )
  .joins(:orders) # sneaky, but works! ????

В более общем случае следующие две строки не будут выполняться.

A.joins(:b).where(bs: b_query).or(A.where(query))  # error! ???? 
A.where(query).or(A.joins(:b).where(bs: b_query))  # error! ???? 

но переставьте следующим образом, и вы сможете уклониться от шашки:

A.where(query).or(A.where(bs: b_query)).joins(:b)  # works ???? 

Это работает, потому что вся проверка происходит внутри метода .or(). Он в блаженном неведении о махинациях со своими последующими результатами.

Один недостаток, конечно, это не так хорошо читается.

person nar8789    schedule 05.03.2021
comment
Это решило проблему для меня. Это действительно должен быть ответом. - person hernan43; 30.03.2021
comment
Исправлено для меня. Спасибо ! соединения и включения должны быть добавлены после или. Может ли кто-нибудь объяснить, почему, если это работает, рельсы не позволяют нам добавить его перед «или»? - person LiKaZ; 12.04.2021
comment
@LiKaZ для joins, я не думаю, что есть веская причина. На самом деле, после rails 6.1.3, я думаю, вы можете поставить .joins перед .or, и я точно знаю, что вы можете поставить .includes перед .or. Что касается того, почему это существует в первую очередь... Rails должен ограничивать определенные операции, такие как .limit или .distinct, так как разрешение их до .or было бы непереводимым в sql. (Что будет означать .or вместе два предложения с разными .limits?) Более подробное объяснение всех случаев здесь: github.com/rails/rails/issues/24055#issuecomment-793274937 - person nar8789; 12.04.2021
comment
@nar8789 спасибо за эти объяснения. Я скоро обновлю свое приложение Rails 6.0 до 6.1.3! - person LiKaZ; 13.04.2021
comment
Было бы интересно узнать, как выглядел сгенерированный SQL для вашего ответа, потому что важнее, чем работать, он делает правильные вещи. - person Nuno Costa; 27.05.2021
comment
@NunoCosta Конечно! вот (частично отредактированный) тест, который я только что запустил в консоли: puts A.where(id: 1).or(A.where(bs: { id: 1 })).joins(:b).to_sql дает SELECT "as".* FROM "as" INNER JOIN "bs" ON "bs"."id" = "as"."b_id" WHERE ("as"."id" = 1 OR "bs"."id" = 1). Это выглядит как хороший SQL для меня. Это выявило ошибку во множественном числе в моих исходных примерах. Я обновлю это сейчас. - person nar8789; 27.05.2021