传递给#or的关系必须在结构上兼容.不兼容的值:[:references]

fro*_*ite 21 sql postgresql activerecord ruby-on-rails ruby-on-rails-5

我有两个查询,我需要or它们之间,即我想要第一个或第二个查询返回的结果.

第一个查询是一个简单的where()获取所有可用项目.

@items = @items.where(available: true)
Run Code Online (Sandbox Code Playgroud)

第二个包括a join()并给出当前用户的项目.

@items =
  @items
  .joins(:orders)
  .where(orders: { user_id: current_user.id})
Run Code Online (Sandbox Code Playgroud)

我尝试以or()各种形式将这些与Rails的方法结合起来,包括:

@items =
  @items
  .joins(:orders)
  .where(orders: { user_id: current_user.id})
  .or(
    @items
    .joins(:orders)
    .where(available: true)
  )
Run Code Online (Sandbox Code Playgroud)

但我一直遇到这个错误,我不知道如何解决它.

Relation passed to #or must be structurally compatible. Incompatible values: [:references]
Run Code Online (Sandbox Code Playgroud)

And*_*eko 19

在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
Run Code Online (Sandbox Code Playgroud)

总是有一个使用SQL的选项:

@items
  .joins(:orders)
  .where(
    "orders.user_id = ? OR items.available = true",
    current_user.id
  )
Run Code Online (Sandbox Code Playgroud)

  • 转而使用SQL查询.谢谢(你的)信息. (2认同)

Raj*_*ngh 9

您可以用这种古老的方式编写查询以避免错误

@items = @items.joins(:orders).where("items.available = ? OR orders.user_id = ?", true, current_user.id)
Run Code Online (Sandbox Code Playgroud)

希望有所帮助!


nar*_*789 7

Hacky 解决方法:.joins.or. 这.joins对检查员隐藏了违规行为。也就是将原题中的代码转换为...

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

Run Code Online (Sandbox Code Playgroud)

更一般地,以下两行都会失败

A.joins(:b).where(bs: b_query).or(A.where(query))  # error!  
Run Code Online (Sandbox Code Playgroud)
A.where(query).or(A.joins(:b).where(bs: b_query))  # error!  
Run Code Online (Sandbox Code Playgroud)

但重新排列如下,你可以逃避检查:

A.where(query).or(A.where(bs: b_query)).joins(:b)  # works  
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为所有检查都发生在.or()方法内部。它很幸运地没有意识到其下游结果的恶作剧。

一个缺点当然是它读起来不太好。