ActiveRecord OU consulta

181

Como você faz uma consulta OR no Rails 3 ActiveRecord. Todos os exemplos que encontro apenas têm consultas AND.

Edit: OR está disponível desde o Rails 5. Consulte ActiveRecord :: QueryMethods

pho3nixf1re
fonte
7
Aprenda SQL. O ActiveRecord facilita a execução das tarefas, mas você ainda precisa saber o que suas consultas estão fazendo, caso contrário, seu projeto poderá não ser dimensionado. Eu também pensei que poderia sobreviver sem precisar lidar com o SQL, e estava muito errado sobre isso.
precisa saber é o seguinte
1
@ ryan0, você está tão certo. Podemos usar gemas sofisticadas e outras coisas, mas devemos estar cientes do que essas gemas estão fazendo por dentro e qual é a tecnologia subjacente, e talvez usar as tecnologias subjacentes sem a ajuda da gema, se necessário, por uma questão de desempenho. As gemas são criadas com um número específico de casos de uso em mente, mas pode haver situações em que um dos casos de uso em nosso projeto possa ser diferente.
rubyprince
2
Podem ser de ajuda: ActiveRecord ou notação Hash consulta
potashin
1
Desde o Rails 5, IMHO, a resposta aceita deve ser a versão de Greg Olsen:Post.where(column: 'something').or(Post.where(other: 'else'))
Philipp Claßen
6
Esta pergunta tem uma tag de ruby-on-rails-3. Por que a resposta aceita se relacionaria apenas ao Rails 5?
iNulty

Respostas:

109

Use o ARel

t = Post.arel_table

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

O SQL resultante:

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%'))
Dan McNevin
fonte
Parece um pouco confuso, mas pelo menos não estou escrevendo sql, o que parece errado! Vou precisar estudar mais o Arel.
Pho3nixf1re 5/09/10
55
Eu não posso beleave quanto mais elegante Sequel é
Macario
3
Não há nada errado com o SQL. O que pode dar errado é como construímos a string SQL, ou seja, Injeção SQL . Portanto, teremos de limpar a entrada do usuário antes de fornecê-la para uma consulta SQL que deve ser executada no banco de dados. Isso será tratado pelos ORMs , e estes têm lidado com os casos extremos, que tendemos a perder. Portanto, é sempre aconselhável usar o ORM para criar consultas SQL.
rubyprince
5
Outro problema com o SQL é que ele não é independente de banco de dados. Por exemplo matches, produzirá LIKEpara sqlite (sem distinção entre maiúsculas e minúsculas por padrão) e ILIKEno postgres (precisa de explícita sem distinção entre maiúsculas e minúsculas como operador).
Amebe 5/02
1
@ToniLeigh ActiveRecord está usando SQL sob o capô. É apenas uma sintaxe para escrever consultas SQL, que lida com a limpeza de SQL e torna suas consultas agnósticas. A única sobrecarga é onde o Rails converte a sintaxe em consulta SQL. E é apenas uma questão de milissegundos. Claro que você deve escrever a consulta SQL com melhor desempenho e tentar convertê-la na sintaxe do ActiveRecord. Se o SQL não puder ser representado na sintaxe do ActiveRecord, você deverá usar o SQL simples.
rubyprince
208

Se você deseja usar um operador OR no valor de uma coluna, pode passar uma matriz para .wheree o ActiveRecord usará IN(value,other_value):

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

saídas:

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

Isso deve atingir o equivalente a um ORem uma única coluna

deadkarma
fonte
13
este é o mais limpo resposta mais "Trilhos do caminho", deveria ter sido aceite
Koonse
10
Obrigado @koonse, não é exatamente uma consulta 'OU', mas produz o mesmo resultado para esta situação.
deadkarma
Ajuda se você pode - stackoverflow.com/questions/15517286/...
Pratik Bothra
80
Esta solução não substitui o OR, pois você não pode usar duas colunas diferentes dessa maneira.
Fotanus
152

no Rails 3, deve ser

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

Isso também inclui sql bruto, mas eu não acho que exista uma maneira no ActiveRecord de fazer a operação OR. Sua pergunta não é uma pergunta noob.

rubyprince
fonte
41
Mesmo se houver sql bruto - essa declaração parece muito mais clara para mim do que a Arel. Talvez até mais seco.
Jibiel # 30/11
6
se você precisa de cadeia, outra tabela junta-se que pode ter as colunas com os nomes das colunas mesmos, você deve usá-lo como esta,Page.where("pages.column = ? or pages.other_column = ?", value, other_value)
rubyprince
1
E isso falhará se a consulta derramar as tabelas. É quando Arel brilha.
DGM
58

Uma versão atualizada do Rails / ActiveRecord pode suportar essa sintaxe nativamente. Seria semelhante a:

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

Conforme observado nesta solicitação de recebimento https://github.com/rails/rails/pull/9052

Por enquanto, basta seguir os seguintes procedimentos:

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

Atualização: de acordo com https://github.com/rails/rails/pull/16052, o orrecurso estará disponível no Rails 5

Atualização: o recurso foi mesclado à ramificação do Rails 5

Christian Fazzini
fonte
2
orestá disponível agora no Rails 5, mas não implementado dessa maneira, pois espera que 1 argumento seja passado. Ele espera um objeto Arel. Veja a resposta aceita
El'Magnifico 4/17/17
2
O ormétodo no ActiveRecord funciona se você o estiver usando diretamente: mas pode quebrar suas expectativas se for usado em um escopo que é encadeado.
Lista Jeremy
O que o @ JeremyList disse está no local. Isso me mordeu com força.
Tribunais20
23

O Rails 5 vem com um ormétodo. (l tinta na documentação )

Este método aceita um ActiveRecord::Relationobjeto. por exemplo:

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

Se você deseja usar matrizes como argumentos, o código a seguir funciona no 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))

Mais informações: OU consulta com matrizes como argumentos no Rails 4 .

Rafał Cieślak
fonte
9
Ou o seguinte: Order.where(query.where_values.inject(:or))usar arel até o fim.
Cameron Martin
> OU consulta com matrizes como argumentos no Rails 4. Link útil! Obrigado!
Zeke Fast
7

O plugin MetaWhere é completamente incrível.

Misture facilmente ORs e ANDs, junte condições em qualquer associação e até especifique OUTER JOIN's!

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

Basta adicionar um OR nas condições

Model.find(:all, :conditions => ["column = ? OR other_column = ?",value, other_value])
Toby Hede
fonte
4
Essa é mais a sintaxe do Rails 2 e requer que eu escreva pelo menos parte de uma string sql.
Pho3nixf1re 5/09/10
3

Acabei de extrair esse plug - in do trabalho do cliente que permite combinar escopos com .or.ex. Post.published.or.authored_by(current_user). Squeel (implementação mais recente do MetaSearch) também é ótimo, mas não permite escopos OR, portanto a lógica da consulta pode ser um pouco redundante.

Woahdae
fonte
3

Com trilhos + arel, uma maneira mais clara:

# 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

Produz:

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

Você poderia fazê-lo como:

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

ou mais elegante, instale rails_or gem e faça o seguinte:

Person.where(:name => 'Pearl').or(:age => 24)
khiav reoy
fonte
-2

Gostaria de acrescentar que esta é uma solução para pesquisar vários atributos de um ActiveRecord. Desde a

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

irá procurar por A e B.

Tomás Gaete
fonte
-4
Book.where.any_of(Book.where(:author => 'Poe'), Book.where(:author => 'Hemingway')
Matthew Rigdon
fonte
2
Adicione algumas explicações à sua resposta
kvorobiev
1
Eu acredito que Matthew estava se referindo à gem activerecord_any_of que adiciona suporte a essa sintaxe.
DannyB
2
Se for verdade, obrigado @DannyB. A resposta deve explicar seu contexto.
Sebastialonso