postgresql 传递给#或的关系必须在结构上兼容,不兼容的值:[:参考文献]

frebpwbc  于 2022-12-12  发布在  PostgreSQL
关注(0)|答案(6)|浏览(131)

我有两个查询,它们之间需要一个or,也就是说,我需要第一个或第二个查询返回的结果。
第一个查询是一个简单的where(),它获取所有可用项。

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

Second包含一个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]
wbrvyc0a

wbrvyc0a1#

有一个known issue about it on 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)
zsohkypk

zsohkypk2#

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

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

希望这对你有帮助!

d8tt03nd

d8tt03nd3#

黑客解决方法:在.or之后执行所有的.joins。这会在检查程序中隐藏有问题的.joins。也就是说,将原始问题中的代码转换为...

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

更一般地说,以下两行都将失败
第一次
但按如下方式重新排列,您就可以避开检查器:

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

这是因为所有的检查都发生在.or()方法内部,它完全不知道下游结果中的诡计。
当然,它的一个缺点是读起来不太好。

xfb7svmp

xfb7svmp4#

我遇到了同样的问题,但是代码是在不同的地方定义的,很难直接更改。

# I can't change "p"
p = Post.where('1 = 1').distinct # this could also be a join

我需要向其中添加一条or语句

p.or(Post.where('2 = 2'))

下面的代码不会引发错误,因为它的distinct与初始关系类似。

p.or(Post.where('2 = 2').distinct)

它的问题在于,只要你知道关系,它就能工作。它可能有也可能没有join,或distinct
无论关系如何,此操作都有效:

p.or(p.unscope(:where).where('2 = 2'))
=> SELECT DISTINCT `posts`.* FROM `posts` WHERE ((1 = 1) OR (2 = 2))
oyt4ldly

oyt4ldly5#

当您尝试合并两个相同类型的多活动记录时,如果其中一个记录具有joins值或includes值,或者在您的情况下具有reference值,而另一个没有,则会发生这种情况。因此,我们需要匹配它们之间的值,我发现了一种通用方法,可以在不事先知道实际值的情况下完成此操作。

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

items_2 = @items.where(available: true)
                .joins(items_1.joins_values)
                .includes(items_1.includes_values)
                .references(items_1.references_values)

@items = items_1.or(items_2)
bksxznpy

bksxznpy6#

解决它!

def exec_or_statement(q1, q2)
    klass = q1.klass
    key = klass.primary_key

    query_wrapper_1 = {}
    query_wrapper_1[key] = q1

    query_wrapper_2 = {}
    query_wrapper_2[key] = q2

    klass.where(query_wrapper_1).or(klass.where(query_wrapper_2))
  end

  query_1 = @items.where(available: true)

  query_2 =
    @items
    .joins(:orders)
    .where(orders: { user_id: current_user.id})
  
  exec_or_statement(query_1, query_2)

相关问题