Rails 3: Как получить все сообщения, идентификаторы которых отсутствуют в данном списке?


Чтобы получить все сообщения с publisher_id равными 10, 16 или 17, я делаю:

Post.where(:publisher_id => [10, 16, 17])

Как бы я получил все сообщения с publisher_id не равно 10, 16 или 17 (то есть все возможные идентификаторы, кроме этих трех) ?

7 6

7 ответов:

Просто выполните a:

Post.where(["publisher_id NOT IN (?)", [10, 16, 17]])

В rails 4 мы можем сделать, как показано ниже

Post.where.not(:publisher_id => [10, 16, 17])

Он будет генерировать SQL, как показано ниже

SELECT "posts".* FROM "posts"  WHERE ("posts"."publisher_id" NOT IN (10, 16, 17))

Непроверено, но должно быть похоже (используя metawhere gem):

Post.where( :id.not_eq => [10,16,17] )

Используя" чистый " синтаксис ActiveRecord, посыпанный Arel с помощью Rails 3, Вы можете сделать что-то вроде этого:

Post.where( Post.arel_table[:publisher_id].not_in([10, 16, 17]) )

Каждый отдельный ответ на этой странице неверен, потому что ни один из этих ответов не заботится обо всех случаях массива, особенно массивы, которые имеют только один элемент.

Вот пример, который будет терпеть неудачу, используя любое из "так называемых" решений на этой странице:

@ids = [1]
Post.where("publisher_id NOT IN (?)", @ids)
#ERROR
Post.where("publisher_id NOT IN (?)", [4])
#ERROR
#...etc

#ALSO
@ids = []
Post.where("publisher_id NOT IN (?)", @ids)
#ERROR
Post.where("publisher_id NOT IN (?)", [])
#ERROR
#...etc

#The problem here is that when the array only has one item, only that element is 
#returned, NOT an array, like we had specified

#Part of the sql that is generated looks like:
#...WHERE (publisher_id NOT IN 166)

#It should be:
#...WHERE (publisher_id NOT IN (166))

Единственный ответ на этой странице, который действительно находится на правильном пути и заботится об этом очень важном деле, - это @Tudor Constantin. но проблема в том, что он на самом деле не показал "способ" использования своей методологии чтобы решить реальный абстрактный пример вопроса, ОП разместил (не просто используя жестко закодированные цифры).

Вот мое решение для динамического поиска идентификаторов, не входящих в Ассоциацию Activerecord, учитывая массив идентификаторов для исключения, который будет работать с массивом из n элементов (...включая n=1 и n=0)

@ids = [166]
@attribute = "publisher_id"
@predicate = "NOT IN"
@ids = "(" + @ids.join(",") + ")"
if @ids == "()"
  #Empty array, just set @ids, @attribute, and @predicate to nil
  @ids = @attribute = @predicate = nil
end

#Finally, make the query
Post.where( [@attribute, @predicate, @ids].join(" ") ) 

#Part of the sql that is generated looks like:
#...WHERE (publisher_id NOT IN (166))
#CORRECT!

#If we had set @ids = []     (empty array)
#Then the if statement sets everything to nil, and then
#rails removes the blank "  " space in the where clause automatically and does
#the query as if all records should be returned, which
#logically makes sense!

Если это помогло вам в любом случае, пожалуйста, голосуйте! Если вы запутались или не поняли один из моих комментариев, пожалуйста, дайте мне знать.

Аккуратное решение, которое я использовал:

ids = #however you get the IDS
Post.where(["id not in (?)", [0,*ids])
  • Наличие 0 означает, что он всегда имеет один элемент в (предполагая, что ничто не имеет идентификатора 0)
  • ID становится splat означает, что он всегда будет массивом.
Post.where(" id NOT IN ( 10, 16, 17) ")