Rails 5: ActiveRecord OR query


Risposte:


228

La possibilità di concatenare orclausole insieme a whereclausole in ActiveRecordquery sarà disponibile in Rails 5 . Vedere la discussione correlata e la richiesta pull .

Quindi, sarai in grado di fare le seguenti cose in Rails 5 :

Per ottenere un postcon id1 o 2:

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

Alcuni altri esempi:

(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
Come posso ottenere (A || B) e& (C || D). Ho provato Post.where (a) .or (Post.where (b)). Where (c) .or (Post.where (d)) ma produce come: (A || B) && C || D
Imran Ahmad

3
@Imran credo che Post.where(a).or(Post.where(b)).where(Post.where(c).or(Post.where(d)))questo dovrebbe creare (a || b) && (c || d)
engineermnky

1
@Imran Questo non sembra funzionare per me: ottengoArgumentError: Unsupported argument type: #<MyModel::ActiveRecord_Relation:0x00007f8edbc075a8> (MyModel::ActiveRecord_Relation)
Suan

5
L'equivalente di .or che prende una relazione e produce un ed è .merge. (A || B) && (C || D) può essere prodotto da 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

Non è necessario attendere che rails 5 utilizzi questa ORquery. Possiamo anche usarlo con rails 4.2.3. C'è un backport qui .

Grazie a Eric-Guo per gem dove-o , ora possiamo aggiungere questa ORfunzionalità >= rails 4.2.3anche usando questa gemma.


6

(Solo un'aggiunta alla risposta di KM Rakibul Islam.)

Usando gli ambiti, il codice può diventare più carino (a seconda degli occhi che guardano):

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

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

5

Avevo bisogno di fare un file (A && B) || (C && D) || (E && F)

Ma allo 5.1.4stato attuale di Rails questo diventa troppo complicato da realizzare con Arel or-chain. Ma volevo comunque utilizzare Rails per generare il maggior numero possibile di query.

Quindi ho fatto un piccolo trucco:

Nel mio modello ho creato un metodo privato chiamato sql_where:

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

Successivamente nel mio ambito ho creato un array per contenere gli 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 '))
}

Che produrrà il risultato della query previsto: SELECT * FROM `models` WHERE ((A AND B) OR (C AND D) OR (E AND F)).

E ora posso combinarlo facilmente con altri ambiti ecc. Senza alcun OR errato.

Il bello è che il mio sql_where accetta normali argomenti della clausola where: sql_where(name: 'John', role: 'admin')genererà (name = 'John' AND role = 'admin').


Penso che tu possa usare .mergecome l'equivalente di && e costruire un albero appropriato per catturare i tuoi genitori. Qualcosa di simile ... (scopeA.merge(scopeB)).or(scopeC.merge(scopeD)).or(scopeE.merge(scopeF)), supponendo che ciascuno degli ambiti assomigli a qualcosa di simileModel.where(...)
nar8789

Controlla questo prima di utilizzare merge - github.com/rails/rails/issues/33501
Aarthi

1

Rails 5 ha capacità di orclausola con where. Per esempio.

User.where(name: "abc").or(User.where(name: "abcd"))
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.