Query OR di ActiveRecord


181

Come si esegue una query OR in Rails 3 ActiveRecord. Tutti gli esempi che trovo hanno solo domande AND.

Modifica: il metodo OR è disponibile da Rails 5. Vedi ActiveRecord :: QueryMethods


7
Impara SQL. ActiveRecord semplifica il funzionamento delle cose, ma è comunque necessario sapere cosa stanno facendo le query, altrimenti il ​​progetto potrebbe non adattarsi. Ho anche pensato di riuscire a cavarmela senza mai avere a che fare con SQL, e mi sbagliavo molto.
ryan0

1
@ ryan0, hai ragione. Potremmo usare gemme fantasiose e altre cose, ma dovremmo essere consapevoli di cosa stanno facendo queste gemme all'interno e quale sia la tecnologia sottostante, e forse usare le tecnologie sottostanti senza l'aiuto della gemma, se necessario, per motivi di prestazione. Le gemme vengono create tenendo presente un numero specifico di casi d'uso, ma potrebbero esserci situazioni in cui uno dei casi d'uso nel nostro progetto potrebbe essere diverso.
rubyprince,


1
Da Rails 5, IMHO la risposta accettata dovrebbe essere la versione di Greg Olsen:Post.where(column: 'something').or(Post.where(other: 'else'))
Philipp Claßen,

6
Questa domanda ha un tag ruby-on-rails-3. Perché la risposta accettata riguarderebbe solo Rails 5?
iNulty

Risposte:


109

Usa ARel

t = Post.arel_table

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

L'SQL risultante:

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%'))

Mi sento un po 'disordinato, ma almeno non sto scrivendo sql, il che mi sembra sbagliato! Dovrò approfondire l'uso di Arel.
pho3nixf1re,

55
Non riesco a credere quanto Sequel sia più elegante
Macario,

3
Non c'è niente di sbagliato in SQL. Ciò che può andare storto è il modo in cui costruiamo la stringa SQL, ovvero SQL Injection . Quindi, dovremo disinfettare l'input dell'utente prima di fornirlo a una query SQL che deve essere eseguita sul database. Questo sarà gestito dagli ORM , e questi hanno gestito i casi limite, che tendiamo a perdere. Pertanto, è sempre consigliabile utilizzare ORM per creare query SQL.
rubyprince,

5
Un altro problema con SQL è che non è indipendente dal database. Ad esempio matchesprodurrà LIKEper sqlite (senza distinzione tra maiuscole e minuscole per impostazione predefinita) e ILIKEsu postgres (necessita di distinzione tra maiuscole e minuscole esplicite come l'operatore).
amebe,

1
@ToniLeigh ActiveRecord sta usando SQL sotto il cofano. È solo una sintassi per la scrittura di query SQL che gestisce la sanificazione SQL e rende agnostiche le query. L'unico sovraccarico è dove Rails converte la sintassi in query SQL. Ed è solo una questione di millisecondi. Ovviamente dovresti scrivere la query SQL con le migliori prestazioni e provare a convertirla nella sintassi di ActiveRecord. Se l'SQL non può essere rappresentato nella sintassi di ActiveRecord, è necessario utilizzare l'SQL normale.
rubyprince,

208

Se si desidera utilizzare un operatore OR sul valore di una colonna, è possibile passare un array a .wheree ActiveRecord utilizzerà IN(value,other_value):

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

uscite:

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

Questo dovrebbe raggiungere l'equivalente di un ORsu una singola colonna


13
questa è la risposta più pulita "rails way", avrebbe dovuto essere accettata
koonse

10
Grazie @koonse, non è esattamente una query "OR", ma produce lo stesso risultato per questa situazione.
deadkarma,


80
Questa soluzione non sostituisce OR poiché non è possibile utilizzare due colonne diverse in questo modo.
fotanus,

152

in Rails 3, dovrebbe essere

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

Ciò include anche sql non elaborato, ma non credo che in ActiveRecord sia possibile eseguire operazioni OR. La tua domanda non è una domanda noob.


41
Anche se c'è sql grezzo - questa affermazione mi sembra molto più chiara di Arel. Forse anche ASCIUGATRICE.
jibiel,

6
se hai bisogno di concatenare, altri join di tabella che potrebbero avere le colonne con gli stessi nomi di colonna, dovresti usarlo in questo modo,Page.where("pages.column = ? or pages.other_column = ?", value, other_value)
rubyprince

1
E ciò non riesce se la query alias le tabelle. Questo è quando Arel brilla.
DGM

58

Una versione aggiornata di Rails / ActiveRecord può supportare questa sintassi in modo nativo. Sembrerebbe simile a:

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

Come indicato in questa richiesta pull https://github.com/rails/rails/pull/9052

Per ora, semplicemente attenersi a quanto segue funziona alla grande:

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

Aggiornamento: secondo https://github.com/rails/rails/pull/16052 la orfunzione sarà disponibile in Rails 5

Aggiornamento: la funzione è stata unita al ramo di Rails 5


2
orè ora disponibile Rails 5 ma non implementato in questo modo perché prevede che venga passato 1 argomento. Si aspetta un oggetto Arel. Vedi la risposta accettata
El'Magnifico

2
Il ormetodo in ActiveRecord funziona se lo stai utilizzando direttamente: ma può infrangere le tue aspettative se viene utilizzato in un ambito che viene quindi incatenato.
Jeremy List,

Quello che ha detto @JeremyList è perfetto. Mi ha morso molto.
courtimas,


23

Rails 5 viene fornito con un ormetodo. (l inchiostro alla documentazione )

Questo metodo accetta un ActiveRecord::Relationoggetto. per esempio:

User.where(first_name: 'James').or(User.where(last_name: 'Scott'))

14

Se si desidera utilizzare le matrici come argomenti, il seguente codice funziona in 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))

Ulteriori informazioni: OPPURE query con matrici come argomenti in Rails 4 .


9
O questo: Order.where(query.where_values.inject(:or))usare arel fino in fondo.
Cameron Martin,

> OPPURE query con matrici come argomenti in Rails 4. Link utile! Grazie!
Zeke Fast,

7

Il plugin MetaWhere è assolutamente sorprendente.

Mescola facilmente OR e AND, unisciti alle condizioni di qualsiasi associazione e specifica anche OUTER JOIN!

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

6

Aggiungi un OR nelle condizioni

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

4
Questa è più la sintassi di Rails 2 e mi richiede di scrivere almeno una parte di una stringa sql.
pho3nixf1re,

3

Ho appena estratto questo plugin dal lavoro client che ti consente di combinare gli ambiti con .or., ad es. Post.published.or.authored_by(current_user). Anche Squeel (la più recente implementazione di MetaSearch) è eccezionale, ma non ti consente gli ambiti OR, quindi la logica della query può diventare un po 'ridondante.


3

Con rails + arel, un modo più chiaro:

# 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

produce:

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

3

Potresti farlo come:

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

o più elegante, installa rails_o gem e fallo come:

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

-2

Vorrei aggiungere questa è una soluzione per cercare più attributi di un ActiveRecord. Da

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

cercherà A e B.


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

2
Aggiungi alcune spiegazioni alla tua risposta
kvorobiev,

1
Credo che Matthew si riferisse alla gemma activerecord_any_of che aggiunge il supporto per questa sintassi.
DannyB,

2
Se vero, grazie @DannyB. La risposta deve spiegare il suo contesto.
Sebastialonso,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.