我正在尝试使用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查询中添加括号没有影响。任何想法吗?
您可以使用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)
我认为根据运算符优先级
问题是AND的优先级高于OR。因此1 AND 2 OR 3
等于(1 AND 2) OR 3
。
作为旁注:如果您使用像这样的包装器,您可以这样写:
User.where((User[:created_at] > 3.days.ago) & (User[:enabled] == true))
为什么不把它们翻过来呢?应该相当于:
participants[:id].is(nil).or(participants[:accepted].eq(false).and(participants[:contact_id].eq(1))
希望我在上面正确地设置了父元素,但是你明白我的意思了…
这里有一个方法,允许您在代码中使用现有的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中也可以。