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

Posted

技术标签:

【中文标题】传递给 #or 的关系必须在结构上兼容。不兼容的值:[:references]【英文标题】:Relation passed to #or must be structurally compatible. Incompatible values: [:references] 【发布时间】:2017-04-06 03:23:26 【问题描述】:

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

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

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

第二个包括join(),并给出当前用户的物品。

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

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

@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]

【问题讨论】:

【参考方案1】:

有一个known issue about it on Github。

根据this comment,您可能需要覆盖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)

【讨论】:

改为使用 SQL 查询。感谢您的信息。 @Andrey 应该参考我的答案,而不是编辑你的答案并添加相同的答案。无论如何,为第一个解决方案 +1。 @RSB 我打算从 sql 部分开始,而不是用谷歌搜索错误消息并发现 github 问题。毕竟我仍然决定添加我最初的想法,因为它的代码更少。【参考方案2】:

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

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

希望有帮助!

【讨论】:

【参考方案3】:

Hacky 解决方法:在.or 之后执行所有.joins。这对检查器隐藏了有问题的.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() 方法内。它对下游结果的恶作剧一无所知。

当然,一个缺点是它的阅读效果不太好。

【讨论】:

这为我解决了这个问题。这真的应该是答案。 已为我修复。谢谢 ! joins 和 includes 必须在 or 之后添加。有人可以解释为什么,如果这可行,rails 不接受我们在 'or' 之前添加它? @LiKaZ for joins,我认为没有什么好的理由。事实上,在rails 6.1.3之后,我认为你可以将.joins放在.or之前,我知道事实上你可以将.includes放在.or之前。至于为什么这首先存在...... Rails必须限制某些操作,如.limit.distinct,因为在.or之前允许这些操作将无法转换为sql。 (.or 将两个具有不同 .limits 的子句放在一起意味着什么?)这里对所有情况的更长解释:github.com/rails/rails/issues/24055#issuecomment-793274937 @nar8789 感谢这些解释。我将尽快将我的 Rails 6.0 应用程序升级到 6.1.3! 如果您想知道生成的 SQL 对于您的答案是什么样子会很有趣,因为除了工作之外,做正确的事情更重要。

以上是关于传递给 #or 的关系必须在结构上兼容。不兼容的值:[:references]的主要内容,如果未能解决你的问题,请参考以下文章

将 WPARAM 传递给 DragQueryFile 不兼容?

指向整数转换的不兼容指针将“SystemSoundID”传递给“SystemSoundID”类型的参数

使用 segue 传递图像的指针类型不兼容?目标c Xcode

为 Objective-C 对象定义 DTrace 兼容结构

不兼容的 Objective-C 类型 - 试图传递 NSURL

* 类型的 C 参数与 * 类型的参数不兼容