Есть ли способ инвертировать запрос ActiveRecord :: Relation? - PullRequest
11 голосов
/ 14 марта 2011

Допустим, у нас есть следующее:

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)"

Ответы [ 5 ]

17 голосов
/ 14 марта 2011

С другим синтаксисом, да. Пример:

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))
6 голосов
/ 21 февраля 2013

В рельсах 4 для этой цели используется суффикс not:

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

В рельсах 3 можно использовать squeel gem .Это дает много полезных функций.И с его помощью вы можете написать:

Post.where{ hidden != true }.to_sql
# => SELECT FROM `posts` WHERE `posts`.`hidden` != 1
5 голосов
/ 12 августа 2011

Мы можем принять ответ Заббы дальше, передав обратный запрос обратно в ActiveRecord:

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

Это вернет объекты ActiveRecord, как вы обычно ожидаете.

0 голосов
/ 21 февраля 2013

Для запросов к одной таблице это будет работать:

query = Post.where(hidden:true)
inverse = Post.where('id not in (?)', query.pluck(:id))

Конечно, мы столкнемся с ужасными проблемами эффективности и не сгенерируем запрашиваемый вами SQL-запрос (хотя, когдавыполнить, это, в конечном итоге, приведет к тому же набору результатов).

0 голосов
/ 11 сентября 2012

Что я делаю, когда ищу записи с условием "не соответствует действительности" (например, ложь или ноль):

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