Как связать запросы области с помощью ИЛИ вместо И? - PullRequest
128 голосов
/ 10 сентября 2010

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

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

например.

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

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

name = 'John' AND lastname = 'Smith'

но я бы хотел:

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

Ответы [ 19 ]

6 голосов
/ 16 июля 2014

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))
4 голосов
/ 19 мая 2014

Рельсы 4

scope :combined_scope, -> { where("name = ? or name = ?", 'a', 'b') }
3 голосов
/ 29 ноября 2013

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

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

(источник: ActiveRecord Query Union )

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

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

2 голосов
/ 16 ноября 2018

Если вы хотите предоставить область (вместо явной работы со всем набором данных), вот что вы должны сделать с 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
2 голосов
/ 26 октября 2016

Также см. Эти связанные вопросы: здесь , здесь и здесь

Для рельсов 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

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

Рельсы 4.1 +

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

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

0 голосов
/ 06 марта 2019

Это очень удобный способ, и он отлично работает в 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")
  )
0 голосов
/ 10 июля 2017

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

Я 'или' область видимости, сгенерированная pg_search, которая делает немного больше, чем select, она включает в себя порядок 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.

0 голосов
/ 11 марта 2013
names = ["tim", "tom", "bob", "alex"]
sql_string = names.map { |t| "name = '#{t}'" }.join(" OR ")
@people = People.where(sql_string)
0 голосов
/ 04 октября 2013

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

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