有没有办法反转 ActiveRecord::Relation 查询?

Posted

技术标签:

【中文标题】有没有办法反转 ActiveRecord::Relation 查询?【英文标题】:Is there a way to invert an ActiveRecord::Relation query? 【发布时间】:2011-07-15 05:20:09 【问题描述】:

假设我们有以下内容:

irb> Post.where(:hidden => true).to_sql
=> "SELECT `posts`.* FROM `posts` WHERE posts.hidden = 1"

我们能否以某种方式从中获得反向 SQL 查询?

我要找的应该是这样的:

irb> Post.where(:hidden => true).invert.to_sql
=> "SELECT `posts`.* FROM `posts` WHERE NOT (posts.hidden = 1)"

【问题讨论】:

嗯。有人对where(:hidden => false) 的评论投了不好的票。该代码将不会生成 OP 正在寻找的 SQL 类型。 【参考方案1】:

invert_where (Rails 7+)

从 Rails 7 开始,有一个新的invert_where 方法。

根据文档,它:

允许您反转整个 where 子句,而不是手动应用条件。

class User
  scope :active, ->  where(accepted: true, locked: false) 
end

User.where(accepted: true)
# WHERE `accepted` = 1

User.where(accepted: true).invert_where
# WHERE `accepted` != 1

User.active
# WHERE `accepted` = 1 AND `locked` = 0

User.active.invert_where
# WHERE NOT (`accepted` = 1 AND `locked` = 0)

要小心,因为这会在调用 invert_where 之前反转所有条件。

class User
  scope :active, ->  where(accepted: true, locked: false) 
  scope :inactive, ->  active.invert_where  # Do not attempt it
end

# It also inverts `where(role: 'admin')` unexpectedly.
User.where(role: 'admin').inactive
# WHERE NOT (`role` = 'admin' AND `accepted` = 1 AND `locked` = 0)

来源:

invert_where 来自官方 Rails API 文档。 Link to the PR。 BigBinary blog article about invert_where method。

【讨论】:

他,我刚刚在谷歌上搜索了一个反向的解决方案,你昨天回答了这个解决方案。迁移到 rails 7 的另一个原因 :)【参考方案2】:

终于有了 Rails 7 的 invert_where 方法。

irb> Post.where(:hidden => true).invert_where.to_sql
"SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"hidden\" != 1"

请查看commit reference了解更多详情。

【讨论】:

【参考方案3】:

在 rails 4 中有 not 用于此目的的后缀:

Post.where.not(hidden: true).to_sql
# => SELECT FROM `posts` WHERE `posts`.`hidden` != 1

在 Rails 3 中,您可以使用 squeel gem。它提供了许多有用的功能。有了它,你可以写:

Post.where hidden != true .to_sql
# => SELECT FROM `posts` WHERE `posts`.`hidden` != 1

【讨论】:

【参考方案4】:

我们可以通过将反向查询传回 ActiveRecord 来进一步处理 Zabba's answer:

table = Post.arel_table
query = table[:hidden].eq(true).not # the inverted query, still ARel
Post.where(query) # plug it back into ActiveRecord

这将返回 ActiveRecord 对象,正如您通常所期望的那样。

【讨论】:

【参考方案5】:

当我查找具有“不真实”条件(例如,假或无)的记录时,我会做的是:

Post.where(["(hidden IS NULL) OR (hidden = ?)", false])

【讨论】:

【参考方案6】:

使用不同的语法,是的。示例:

posts = Post.scoped.table # or Arel::Table.new("posts")
posts.where(posts[:hidden].eq(true).not).to_sql
# => SELECT  FROM `posts` WHERE NOT ((`posts`.`hidden` = 1))

【讨论】:

不是我想要的,但仍然是一个很好的答案。 您找到其他方法了吗?也许更好的语法?答案对你有用吗? 实际上我认为这甚至是不可能的,因为我实际上要求的是反转和 ActiveRecord::Relation 对象,它可能有也可能没有多个连接,并且包含会使事情变得过于复杂(其中 WHEREs我们应该倒置,哪个不倒置?)。我想我会保持开放状态,直到出现答案。

以上是关于有没有办法反转 ActiveRecord::Relation 查询?的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法将用户输入存储在数组中并反转它? [复制]

有没有办法在java中的多维数组中反转特定数组?

WebGL 中有没有办法快速反转模板缓冲区?

如何使用 CSS 反转颜色?

反转图例的顺序

反转 UIBarButtonItem“播放”以用作后退按钮?