在 AREL 中对 and 和 or 进行分组

问题描述 投票:0回答:4

我正在尝试使用 arel 查询此 sql 片段的等效项:

WHERE (("participants"."accepted" = 'f' AND "participants"."contact_id" = 1) 
  OR "participants"."id" IS NULL)

所以我想要

(accepted && contact_id=1) OR NULL

这是我在 AREL 中获得的内容

participants[:accepted].eq(false).and(participants[:contact_id].eq(1).
  or(participants[:id].is(nil)

问题是,这会产生:

("participants"."accepted" = 'f' AND "participants"."contact_id" = 1 OR "participants"."id" IS NULL)

请注意“我”和“条件”周围缺少括号。我相信根据运算符优先级,我得到:

accepted && (contact_id=1 OR NULL)

在 AREL 查询中添加括号没有任何影响。有什么想法吗?

sql ruby arel
4个回答
31
投票

您可以使用

Arel::Nodes::Grouping
生成括号。

participants = Arel::Table.new("participants")

arel = participants.grouping(
  participants[:accepted].eq(false).and(participants[:contact_id].eq(1))
).or(participants[:id].eq(nil))

arel.to_sql # => (("participants"."accepted" = 'f' AND "participants"."contact_id" = 1) OR "participants"."id" IS NULL)

8
投票

我相信根据运算符优先级

问题在于AND 的优先级高于OR。所以

1 AND 2 OR 3
相当于
(1 AND 2) OR 3

附注:如果您使用像this这样的包装器,您可以写:

User.where((User[:created_at] > 3.days.ago) & (User[:enabled] == true))

2
投票

为什么不把它们翻转过来呢?应该相当于:

participants[:id].is(nil).or(participants[:accepted].eq(false).and(participants[:contact_id].eq(1))

希望我已经在上面正确设置了括号,但你明白我的意思......


0
投票

这里有一个方法,允许您在代码中使用现有的 AR 方法,例如

where
以及任何现有的范围等。因此,可以根据需要轻松地通过 Arel 重用现有 AR 代码。

class Participant
  scope :grouped, -> {
    group1 = arel_table.grouping(where(accepted: false, contact_id: 1).arel.constraints)
    group2 = arel_table.grouping(where(id: nil).arel.constraints)

    where(group1.or(group2))
  }
end

这适用于 Rails 7,或许也适用于 Rails 6.x。

© www.soinside.com 2019 - 2024. All rights reserved.