ActiveRecord АБО запит


181

Як зробити запит АБО у Rails 3 ActiveRecord. Усі приклади, які я знаходжу, просто мають І запити.

Правка: АБО метод доступний з Rails 5. Див. ActiveRecord :: QueryMethods


7
Вивчіть SQL. ActiveRecord полегшує роботу, але ви все ще повинні знати, що роблять ваші запити, інакше ваш проект може не змінювати масштаб. Я також думав, що можу обійтись, не маючи стосунку до SQL, і я дуже помилявся з цим.
ryan0

1
@ ryan0, ти так прав. Ми можемо використовувати вигадливі дорогоцінні камені та інші речі, але ми повинні знати, що ці дорогоцінні камені роблять всередині і що є базовою технологією, і, можливо, використовувати базові технології без допомоги дорогоцінного каміння, якщо це може виникнути, якщо це виникне потреба виконання. Камені створені з певною кількістю випадків використання на увазі, але можуть бути ситуації, коли один із випадків використання в нашому проекті може бути іншим.
rubyprince

2
Можуть допомогти: ActiveRecord АБО запит Hash notation
potashin

1
Оскільки Rails 5, IMHO прийнятою відповіддю має бути версія Грега Олсена:Post.where(column: 'something').or(Post.where(other: 'else'))
Philipp Claßen

6
Це питання має тег ruby-on-rails-3. Чому прийнята відповідь стосується лише Рейки 5?
iNulty

Відповіді:


109

Використовуйте ARel

t = Post.arel_table

results = Post.where(
  t[:author].eq("Someone").
  or(t[:title].matches("%something%"))
)

Отриманий SQL:

ree-1.8.7-2010.02 > puts Post.where(t[:author].eq("Someone").or(t[:title].matches("%something%"))).to_sql
SELECT     "posts".* FROM       "posts"  WHERE     (("posts"."author" = 'Someone' OR "posts"."title" LIKE '%something%'))

Відчуває себе трохи безладним, але принаймні я не пишу sql, що просто відчуває себе неправильно! Мені доведеться більше вивчити використання Arel.
pho3nixf1re

55
Я не можу заперечувати, наскільки елегантніший Sequel
Макаріо

3
У SQL немає нічого поганого. Те, що може піти не так - це те, як ми будуємо рядок SQL, а саме SQL Injection . Отже, нам доведеться провести санітарний вклад користувача перед тим, як надати його SQL-запиту, який повинен запускатися проти бази даних. Цим будуть займатися ORM , і вони займаються кращими справами, які ми, як правило, пропускаємо. Отже, ORM завжди доцільно використовувати ORM для створення запитів SQL.
rubyprince

5
Ще одна проблема SQL полягає в тому, що це не агностик бази даних. Наприклад matchesбуде вироблятися LIKEдля sqlite (регістр нечутливий до замовчування) та ILIKEдля postgres (потрібен явний нечутливий регістр, як оператор).
амебе

1
@ToniLeigh ActiveRecord використовує SQL під кришкою. Це просто синтаксис для запису SQL запитів, який обробляє санітарію SQL та робить ваші запити БД агностичними. Єдина накладні витрати - це те, де Rails перетворює синтаксис у SQL-запит. І це лише питання мілісекунд. Звичайно, ви повинні написати найкращий SQL-запит і спробувати перетворити його в синтаксис ActiveRecord. Якщо SQL не може бути представлений у синтаксисі ActiveRecord, то слід перейти до звичайного SQL.
rubyprince

208

Якщо ви хочете використовувати оператор АБО для значення одного стовпця, ви можете передати масив .whereі ActiveRecord буде використовувати IN(value,other_value):

Model.where(:column => ["value", "other_value"]

Виходи:

SELECT `table_name`.* FROM `table_name` WHERE `table_name`.`column` IN ('value', 'other_value')

Це повинно досягти еквівалента знаку ORв одному стовпчику


13
це найчистіша відповідь на "рейковий шлях", її слід було прийняти
koonse

10
Дякую @koonse, це не зовсім запит "АБО", але він дає такий же результат для цієї ситуації.
deadkarma

Допоможіть, якщо зможете - stackoverflow.com/questions/15517286/…
Pratik Bothra

80
Це рішення не є заміною для АБО, оскільки ви не можете використовувати два різні стовпці таким чином.
fotanus

152

у рейках 3, так і повинно бути

Model.where("column = ? or other_column = ?", value, other_value)

Сюди також входить необроблений sql, але я не думаю, що в ActiveRecord є спосіб зробити АБО операцію. Ваше запитання не є питанням noob.


41
Навіть якщо є сировина sql - це твердження для мене виглядає набагато чіткіше, ніж Arel. Можливо навіть DRYer.
jibiel

6
якщо вам потрібно ланцюг, приєднується інша таблиця, у якій можуть бути стовпці з однаковими назвами стовпців, ви повинні використовувати її так,Page.where("pages.column = ? or pages.other_column = ?", value, other_value)
rubyprince

1
І це не вдається, якщо запит псевдонімує таблиці. Ось тоді світить Арель.
ГДМ

58

Оновлена ​​версія Rails / ActiveRecord може підтримувати цей синтаксис спочатку. Це буде схоже на:

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

Як зазначено в цьому запиті на витяг https://github.com/rails/rails/pull/9052

Наразі просто дотримуватися наступних дій чудово:

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

Оновлення: По https://github.com/rails/rails/pull/16052or функція буде доступна в Rails 5

Оновлення: функцію було об’єднано у відділення Rails 5


2
orдоступно зараз Rails 5, але не реалізовано таким чином, оскільки він очікує, що буде передано 1 аргумент. Він очікує об’єкт Arel. Дивіться прийняту відповідь
El'Magnifico

2
orМетод ActiveRecord працює , якщо ви використовуєте його прямо: а може порушити ваші очікування , якщо він використовується в обсязі , який потім прикутий.
Список Джеремі

Те, що сказав @JeremyList, це місце. Це мене важко покусало.
Courtimas



14

Якщо ви хочете використовувати масиви як аргументи, наступний код працює в Rails 4:

query = Order.where(uuid: uuids, id: ids)
Order.where(query.where_values.map(&:to_sql).join(" OR "))
#=> Order Load (0.7ms)  SELECT "orders".* FROM "orders" WHERE ("orders"."uuid" IN ('5459eed8350e1b472bfee48375034103', '21313213jkads', '43ujrefdk2384us') OR "orders"."id" IN (2, 3, 4))

Додаткова інформація: АБО запити з масивами як аргументами в Rails 4 .


9
Або це: Order.where(query.where_values.inject(:or))користуватися arel весь шлях.
Камерон Мартін

> АБО запити з масивами як аргументами в Rails 4. Корисне посилання! Дякую!
Zeke Fast

7

MetaWhere плагін зовсім дивно.

Легко змішуйте АБО та І, приєднуйтесь до умов будь-якої асоціації та навіть вказуйте ІХ ВАРТІСТЬ!

Post.where({sharing_level: Post::Sharing[:everyone]} | ({sharing_level: Post::Sharing[:friends]} & {user: {followers: current_user} }).joins(:user.outer => :followers.outer}

6

Просто додайте АБО в умовах

Model.find(:all, :conditions => ["column = ? OR other_column = ?",value, other_value])

4
Це більше синтаксис для Rails 2, і він вимагає від мене написати принаймні частину рядка sql.
pho3nixf1re

3

Я щойно витягнув цей плагін із роботи з клієнтами, що дозволяє поєднувати сфери застосування .or., наприклад. Post.published.or.authored_by(current_user). Squeel (новіша реалізація MetaSearch) також чудовий, але не дає вам АБО області застосування, тому логіка запитів може стати трохи зайвою.


3

З рейки + арел більш чіткий спосіб:

# Table name: messages
#
# sender_id:    integer
# recipient_id: integer
# content:      text

class Message < ActiveRecord::Base
  scope :by_participant, ->(user_id) do
    left  = arel_table[:sender_id].eq(user_id)
    right = arel_table[:recipient_id].eq(user_id)

    where(Arel::Nodes::Or.new(left, right))
  end
end

Виробляє:

$ Message.by_participant(User.first.id).to_sql 
=> SELECT `messages`.* 
     FROM `messages` 
    WHERE `messages`.`sender_id` = 1 
       OR `messages`.`recipient_id` = 1

3

Ви можете це зробити так:

Person.where("name = ? OR age = ?", 'Pearl', 24)

або більш елегантний, встановіть дорогоцінний камінь rails_or і зробіть це так:

Person.where(:name => 'Pearl').or(:age => 24)

-2

Я хотів би додати, що це рішення для пошуку декількох атрибутів ActiveRecord. З тих пір

.where(A: param[:A], B: param[:B])

буде шукати A і B.


-4
Book.where.any_of(Book.where(:author => 'Poe'), Book.where(:author => 'Hemingway')

2
Додайте кілька пояснень до своєї відповіді
kvorobiev

1
Я вважаю, що Метью мав на увазі дорогоцінний камінь activerecord_any_of, який додає підтримку цього синтаксису.
DannyB

2
Якщо це правда, дякую @DannyB. Відповідь повинна пояснювати її контекст.
Себастіалонсо
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.