【问题标题】:Relation passed to #or must be structurally compatible. Incompatible values: [:references]传递给 #or 的关系必须在结构上兼容。不兼容的值:[: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]

【问题讨论】:

    标签: sql ruby-on-rails postgresql activerecord ruby-on-rails-5


    【解决方案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 对于您的答案是什么样子会很有趣,因为除了工作之外,做正确的事情更重要。
      猜你喜欢
      • 2019-06-26
      • 2018-02-25
      • 2021-01-22
      • 2019-12-16
      • 2021-10-24
      • 1970-01-01
      • 2022-11-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多