Rails 不查询整个 Where 子句

Rails Not query on entire Where clause

是否有直接的方法使用 ActiveRecord/ARel 否定整个 where 表达式?似乎 where.not(),当给定参数的散列时,会单独否定每个表达式,而不是用单个 SQL NOT.

来否定整个事物

Rails 例子

Thing.where.not(attribute1: [1,2], attribute2: [3,4], attribute3: [5,6])

会产生 SQL:

select * from things where attribute1 NOT IN (1,2) AND attribute2 NOT IN (3,4) AND attribute3 NOT IN (5,6)

但这不是我想要做的。我想用一个 NOT.

否定整个 where 子句

select * from things where NOT(attribute1 IN (1,2) AND attribute2 IN (3,4) AND attribute3 IN (5,6))

在布尔符号中,Rails 似乎倾向于否定 WHERE 子句的每个组成部分,如下所示:

!(a) && !(b) && !(c)

但我想否定整个表达式:

! [ (a) && (b) && (c) ]

使用 DeMorgan 定律,我可以将查询写成 !a || !b || !c,但这会导致一些相当长且难看的代码(Rails 5 和 or 的代码不那么长,但是还是丑)。我希望使用 ActiveRecord 或 ARel 时缺少一些语法糖?

背景故事

我正在编写 Ransack Equality Scope(例如 _eq)来搜索条件及其相反条件。

scope :can_find_things_eq, ->(boolean = true) {
  case boolean
    when true, 'true'
      where(conditions_for_things)
    when false, 'false'
      where.not(conditions_for_things)
    end
  }

def self.ransackable_scopes(_auth_object = nil)
  %i[can_find_things_eq]
end

如果我使用我上面的 Rails 5 建议和我开始的例子,我可以让我的否定查询工作......但是代码又长又难看。

where.not(attribute1: [1,2]).or(where.not(attribute2:
  [3,4)).or(where.not(attribute3: [5,6))

链接 Ors 和 WhereNots 可行,但可读性不佳。除了必须使用 DeMorgan 定律来否定它 manually/logically 之外,还有更好的方法来否定这个 where 吗?

谢谢!

您可以使用 Arel 生成所需的结果,如下所示:

table = Thing.arel_table
clause = Arel::Nodes::Not.new(
  table[:attribute1].in([1,2]).and(
    table[:attribute2].in([2,3]).and(
      table[:attribute3].in([5,6])
    )))
Thing.where(clause)

或者

Thing.where.not(
  table[:attribute1].in([1,2]).and(
    table[:attribute2].in([2,3]).and(
      table[:attribute3].in([5,6])
    )))

如果你正在使用Rails7,你可以使用invert_where。这将完全按照您的需要形成查询

! [ (a) && (b) && (c) ]