Rails 5: ActiveRecord OU consulta


Respostas:


228

A capacidade de encadear orcláusula junto com a wherecláusula na ActiveRecordconsulta estará disponível no Rails 5 . Veja a discussão relacionada e a solicitação de pull .

Então, você será capaz de fazer as seguintes coisas no Rails 5 :

Para obter um postcom id1 ou 2:

Post.where('id = 1').or(Post.where('id = 2'))

Alguns outros exemplos:

(A && B) || C:

    Post.where(a).where(b).or(Post.where(c))

(A || B) && C:

    Post.where(a).or(Post.where(b)).where(c)

3
Como posso obter (A || B) && (C || D). Eu tentei Post.where (a) .or (Post.where (b)). Where (c) .or (Post.where (d)) mas produz como: (A || B) && C || D
Imran Ahmad

3
@Imran, acredito que seria Post.where(a).or(Post.where(b)).where(Post.where(c).or(Post.where(d)))isso deve criar (a || b) && (c || d)
engineeringmnky

1
@Imran Isso não parece funcionar para mim: Eu entendoArgumentError: Unsupported argument type: #<MyModel::ActiveRecord_Relation:0x00007f8edbc075a8> (MyModel::ActiveRecord_Relation)
Suan

5
O equivalente a .or que pega uma relação e produz um e é .merge. (A || B) && (C || D) pode ser produzido por Post.where (a) .or (Post.where (b)). Merge (Post.where (c) .or (Post.where (d )))
Siim Liiser

2
@MathieuJ. É ActiveRecord :: Relation # merge. api.rubyonrails.org/classes/ActiveRecord/…
Siim Liiser

13

Não precisamos esperar pelo Rails 5 para usar esta ORconsulta. Também podemos usá-lo com rails 4.2.3. Há um backport aqui .

Agradeço a Eric-Guo por gem where-or , Agora podemos adicionar essa ORfuncionalidade ao >= rails 4.2.3usar esta gem.


6

(Apenas um acréscimo à resposta de KM Rakibul Islam.)

Usando escopos, o código pode se tornar mais bonito (dependendo da aparência dos olhos):

scope a,      -> { where(a) }
scope b,      -> { where(b) }

scope a_or_b, -> { a.or(b) }

5

Eu precisava fazer um (A && B) || (C && D) || (E && F)

Mas no 5.1.4estado atual de Rails , isso é muito complicado de se realizar com o Arel ou cadeia. Mas eu ainda queria usar o Rails para gerar o máximo de consulta possível.

Então, fiz um pequeno hack:

No meu modelo, criei um método privado chamado sql_where:

private
  def self.sql_where(*args)
    sql = self.unscoped.where(*args).to_sql
    match = sql.match(/WHERE\s(.*)$/)
    "(#{match[1]})"
  end

Em seguida, no meu escopo, criei uma matriz para conter os OR

scope :whatever, -> {
  ors = []

  ors << sql_where(A, B)
  ors << sql_where(C, D)
  ors << sql_where(E, F)

  # Now just combine the stumps:
  where(ors.join(' OR '))
}

Que irá produzir o resultado da consulta esperado: SELECT * FROM `models` WHERE ((A AND B) OR (C AND D) OR (E AND F)).

E agora posso facilmente combinar isso com outros osciloscópios, etc., sem nenhum OR incorreto.

A beleza é que meu sql_where aceita argumentos normais da cláusula where: sql_where(name: 'John', role: 'admin')will generate (name = 'John' AND role = 'admin').


Acho que você pode usar .mergecomo o equivalente a && e construir uma árvore adequada para capturar seus parênteses. Algo como ... (scopeA.merge(scopeB)).or(scopeC.merge(scopeD)).or(scopeE.merge(scopeF)), supondo que cada um dos escopos se pareça comModel.where(...)
nar8789

Verifique isso antes de usar mesclar - github.com/rails/rails/issues/33501
Aarthi

1

Rails 5 tem capacidade para orcláusula com where. Por exemplo.

User.where(name: "abc").or(User.where(name: "abcd"))
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.