Как связать запросы области с помощью ИЛИ вместо И?

Я использую Rails3, ActiveRecord

Просто интересно, как я могу связать области видимости с помощью операторов OR вместо AND.

например

Person.where(:name => "John").where(:lastname => "Smith")

Это обычно возвращает:

name = 'John' AND lastname = 'Smith'

но я бы хотел:

'name = 'John' OR lastname = 'Smith'

Ответ 1

Вы бы сделали

Person.where('name=? OR lastname=?', 'John', 'Smith')

В настоящий момент нет никакой другой поддержки OR новым синтаксисом AR3 (который не использует какой-то сторонний камень).

Ответ 2

Используйте ARel

t = Person.arel_table

results = Person.where(
  t[:name].eq("John").
  or(t[:lastname].eq("Smith"))
)

Ответ 3

В соответствии с этот запрос на перенос, Rails 5 теперь поддерживает следующий синтаксис для цепочки запросов:

Post.where(id: 1).or(Post.where(id: 2))

В Rails 4.2 также имеется обратная связь с функциями этот камень.

Ответ 4

Обновление для Rails4

не требует никаких сторонних драгоценных камней

a = Person.where(name: "John") # or any scope 
b = Person.where(lastname: "Smith") # or any scope 
Person.where([a, b].map{|s| s.arel.constraints.reduce(:and) }.reduce(:or))\
  .tap {|sc| sc.bind_values = [a, b].map(&:bind_values) }

Старый ответ

не требует никаких сторонних драгоценных камней

Person.where(
    Person.where(:name => "John").where(:lastname => "Smith")
      .where_values.reduce(:or)
)

Ответ 5

Просто отправьте синтаксис Array для столбца того же или запросов, чтобы помочь заглянуть.

Person.where(name: ["John", "Steve"])

Ответ 6

В случае, если кто-то ищет обновленный ответ на этот вопрос, похоже, что существует существующий запрос на pull для его получения в Rails: https://github.com/rails/rails/pull/9052.

Благодаря @j-mcnally патчу обезьяны для ActiveRecord (https://gist.github.com/j-mcnally/250eaaceef234dd8971b) вы можете сделать следующее:

Person.where(name: 'John').or.where(last_name: 'Smith').all

Еще более ценной является возможность привязки областей с помощью OR:

scope :first_or_last_name, ->(name) { where(name: name.split(' ').first).or.where(last_name: name.split(' ').last) }
scope :parent_last_name, ->(name) { includes(:parents).where(last_name: name) }

Затем вы можете найти всех лиц с фамилией или фамилией или родителем с фамилией

Person.first_or_last_name('John Smith').or.parent_last_name('Smith')

Не лучший пример для использования этого, но просто пытается подогнать его к вопросу.

Ответ 7

Вы также можете использовать MetaWhere gem, чтобы не смешивать ваш код с материалом SQL:

Person.where((:name => "John") | (:lastname => "Smith"))

Ответ 8

Для меня (Rails 4.2.5) он работает только так:

{ where("name = ? or name = ?", a, b) }

Ответ 9

Это будет хорошим кандидатом для MetaWhere, если вы используете Rails 3.0+, но он не работает на Rails 3.1. Возможно, вы захотите попробовать squeel. Это сделал тот же автор. Здесь, как бы вы использовали цепочку на основе OR:

Person.where{(name == "John") | (lastname == "Smith")}

Вы можете смешивать и сопоставлять AND/OR среди многих других удивительных вещей.

Ответ 10

Обновленная версия Rails/ActiveRecord может поддерживать этот синтаксис изначально. Он будет выглядеть так:

Foo.where(foo: 'bar').or.where(bar: 'bar')

Как отмечено в этом запросе на растяжение https://github.com/rails/rails/pull/9052

Пока просто отлично соблюдайте следующие работы:

Foo.where('foo= ? OR bar= ?', 'bar', 'bar')

Ответ 11

Rails 4 + Scope + Arel

class Creature < ActiveRecord::Base
    scope :is_good_pet, -> {
        where(
            arel_table[:is_cat].eq(true)
            .or(arel_table[:is_dog].eq(true))
            .or(arel_table[:eats_children].eq(false))
        )
    }
end

Я попытался связать имена областей с .or и не повезло, но это сработало для поиска чего-либо с этими булевыми наборами. Создает SQL как

SELECT 'CREATURES'.* FROM 'CREATURES' WHERE ((('CREATURES'.'is_cat' = 1 OR 'CREATURES'.'is_dog' = 1) OR 'CREATURES'.'eats_children' = 0))

Ответ 12

Рельсы 4

scope :combined_scope, -> { where("name = ? or name = ?", 'a', 'b') }

Ответ 13

Если вы не можете записать предложение where вручную, чтобы включить оператор "или" (т.е. вы хотите объединить две области), вы можете использовать union:

Model.find_by_sql("#{Model.scope1.to_sql} UNION #{Model.scope2.to_sql}")

(источник: Союз запросов ActiveRecord)

Это будет возвращать все записи, соответствующие любому запросу. Однако это возвращает массив, а не isl. Если вы действительно хотите вернуть isl, вы проверяете эту суть: https://gist.github.com/j-mcnally/250eaaceef234dd8971b.

Это будет выполнять эту работу, если вы не возражаете против перехвата обезьян.

Ответ 14

Также см. следующие вопросы: здесь, здесь и здесь

Для рельсов 4, на основе эта статья и этот оригинальный ответ

Person
  .unscoped # See the caution note below. Maybe you want default scope here, in which case just remove this line.
  .where( # Begin a where clause
    where(:name => "John").where(:lastname => "Smith")  # join the scopes to be OR'd
    .where_values  # get an array of arel where clause conditions based on the chain thus far
    .inject(:or)  # inject the OR operator into the arels 
    # ^^ Inject may not work in Rails3. But this should work instead:
    .joins(" OR ")
    # ^^ Remember to only use .inject or .joins, not both
  )  # Resurface the arels inside the overarching query

Примечание в конце статьи:

Rails 4.1 +

Rails 4.1 рассматривает default_scope как обычную область. По умолчанию область (если она есть) включена в результат where_values ​​и inject (: or) будет добавлять или указывать между областью действия по умолчанию и вашим Wheres. Это плохо.

Чтобы решить эту проблему, вам просто нужно отменить запрос.

Ответ 15

Если вы хотите предоставить область действия (вместо явной работы над всем набором данных), то, что вы должны сделать с Rails 5:

scope :john_or_smith, -> { where(name: "John").or(where(lastname: "Smith")) }

Или же:

def self.john_or_smith
  where(name: "John").or(where(lastname: "Smith"))
end

Ответ 16

squeel gem обеспечивает невероятно простой способ выполнить это (до этого я использовал что-то вроде метода @coloradoblue):

names = ["Kroger", "Walmart", "Target", "Aldi"]
matching_stores = Grocery.where{name.like_any(names)}

Ответ 17

Итак, ответ на исходный вопрос, можете ли вы присоединиться к областям с помощью "или" вместо "и", кажется, "нет, вы не можете". Но вы можете передать код совершенно другой области или запроса, который выполняет задание, или использовать другую структуру из ActiveRecord, например. MetaWhere или Squeel. Не полезно в моем случае

Я 'or'ing область, сгенерированную pg_search, которая делает немного больше, чем выбор, она включает в себя порядок от ASC, что делает беспорядок чистого соединения. Я хочу "или" с областью, созданной вручную, которая делает вещи, которые я не могу сделать в pg_search. Поэтому я должен был сделать это вот так.

Product.find_by_sql("(#{Product.code_starts_with('Tom').to_sql}) union (#{Product.name_starts_with('Tom').to_sql})")

т.е. превратите области в sql, поместите скобки вокруг каждого из них, объедините их вместе, а затем find_by_sql, используя сгенерированный sql. Это немного мусор, но он работает.

Нет, не говорите мне, что я могу использовать "против: [: name,: code]" в pg_search, я хотел бы сделать это так, но поле "name" - это hstore, который pg_search может Пока не обращайся. Таким образом, область по имени должна быть обработана вручную, а затем объединена с областью pg_search.

Ответ 18

Это очень удобный способ, и он отлично работает в Rails 5:

Transaction
  .where(transaction_type: ["Create", "Correspond"])
  .or(
    Transaction.where(
      transaction_type: "Status",
      field: "Status",
      newvalue: ["resolved", "deleted"]
    )
  )
  .or(
    Transaction.where(transaction_type: "Set", field: "Queue")
  )

Ответ 19

names = ["tim", "tom", "bob", "alex"]
sql_string = names.map { |t| "name = '#{t}'" }.join(" OR ")
@people = People.where(sql_string)