web-dev-qa-db-de.com

Kombinieren Sie zwei ActiveRecord :: Relation-Objekte

Angenommen, ich habe die folgenden zwei Objekte:

first_name_relation = User.where(:first_name => 'Tobias') # ActiveRecord::Relation
last_name_relation  = User.where(:last_name  => 'Fünke') # ActiveRecord::Relation

kann man die beiden Beziehungen kombinieren, um ein ActiveRecord::Relation-Objekt zu erzeugen, das beide Bedingungen enthält?

Hinweis: Ich bin mir bewusst, dass ich die Ketten ketten kann, um dieses Verhalten zu erhalten. Was mich wirklich interessiert, ist der Fall, wenn ich zwei separate ActiveRecord::Relation-Objekte habe.

150

Wenn Sie mit AND (Kreuzung) kombinieren möchten, verwenden Sie merge :

first_name_relation.merge(last_name_relation)

Wenn Sie OR (union) verwenden möchten, verwenden Sie or (verfügbar in ActiveRecord 5+ oder in 4.2 über where-oder backport ):

first_name_relation.or(last_name_relation)
166
Andrew Marshall

Beziehungsobjekte können in Arrays konvertiert werden. Dies macht es jedoch unmöglich, ActiveRecord-Methoden darauf anzuwenden, dies war jedoch nicht erforderlich. Ich tat dies:

name_relation = first_name_relation + last_name_relation

Ruby 1.9, Rails 3.2

35
thatmiddleway

merge funktioniert eigentlich nicht wie OR. Es ist einfach ein Schnittpunkt (AND

Ich habe mit diesem Problem gekämpft, um ActiveRecord :: Relation-Objekte zu einem zu kombinieren, und ich habe keine funktionierende Lösung für mich gefunden. 

Anstatt nach der richtigen Methode zu suchen, um aus diesen beiden Mengen eine Vereinigung zu erstellen, konzentrierte ich mich auf die Algebra von Mengen. Sie können dies auf unterschiedliche Weise tun, indem Sie das Gesetz von De Morgan

ActiveRecord stellt die Merge-Methode (AND) bereit, und Sie können auch keine Methode oder none_of (NOT) verwenden. 

search.where.none_of(search.where.not(id: ids_to_exclude).merge(search.where.not("title ILIKE ?", "%#{query}%")))

Du hast hier (A u B) '= A' ^ B '

UPDATE: Die Lösung oben eignet sich für komplexere Fälle. In Ihrem Fall wird es so sein:

User.where('first_name LIKE ? OR last_name LIKE ?', 'Tobias', 'Fünke')
18

Ich habe dies selbst in vielen ungeraden Situationen durch die Verwendung von Rails '- Arel erreichen können.

User.where(
  User.arel_table[:first_name].eq('Tobias').or(
    User.arel_table[:last_name].eq('Fünke')
  )
)

Dadurch werden beide ActiveRecord-Beziehungen unter Verwendung von Arels oder zusammengeführt.


Merge hat, wie hier vorgeschlagen wurde, für mich nicht funktioniert. Die 2. Gruppe von Beziehungsobjekten wurde aus den Ergebnissen entfernt.

14
6ft Dan

Es gibt einen Edelstein namens active_record_union Nach dem Sie suchen. 

Es gibt folgende Beispielverwendungen:

current_user.posts.union(Post.published)
current_user.posts.union(Post.published).where(id: [6, 7])
current_user.posts.union("published_at < ?", Time.now)
user_1.posts.union(user_2.posts).union(Post.published)
user_1.posts.union_all(user_2.posts)
13
echo

Wenn Sie über eine Reihe von Aktivitätsaufzeichnungen verfügen und diese zusammenführen möchten, können Sie dies tun 

array.inject(:merge)
6
mr.musicman

So habe ich es "behandelt", wenn Sie pluck verwenden, um einen Bezeichner für jeden Datensatz zu erhalten, die Arrays zusammenzufügen und anschließend eine Abfrage für diese verbundenen IDs durchführen:

  transaction_ids = @club.type_a_trans.pluck(:id) + @club.type_b_transactions.pluck(:id) + @club.type_c_transactions.pluck(:id)
  @transactions = Transaction.where(id: transaction_ids).limit(100)
6
daveomcd

Brute zwinge es:

first_name_relation = User.where(:first_name => 'Tobias') # ActiveRecord::Relation
last_name_relation  = User.where(:last_name  => 'Fünke') # ActiveRecord::Relation

all_name_relations = User.none
first_name_relation.each do |ar|
  all_name_relations.new(ar)
end
last_name_relation.each do |ar|
  all_name_relations.new(ar)
end
0

Ich bin schon ein paar Mal auf dieses Thema gestoßen und die allgemein akzeptierten Antworten haben für mich nie ganz funktioniert. 

Die vorgeschlagene Lösung in der akzeptierten Antwort schien am besten zu funktionieren: 

first_name_relation.or(last_name_relation)

Aber ich konnte das nie zum Laufen bringen und erhielt immer einen "undefined method" -Fehler für "oder". 

Meine Lösung:

combined_relation = first_name_relation + (last_name_relation - first_name_relation)

Dies gibt mir im Wesentlichen die Ergebnisse einer UNION zwischen den ursprünglichen Abfragen und den Beziehungsobjekten sind immer noch die korrekten ActiveRecord-Objekte. 

Dies ist ähnlich der Antwort von @thatmiddleway, obwohl ich die Subtraktion hinzufügen musste, um eine DISTINCT-Sammlung von Objekten zu erhalten. Dadurch wird die kombinierte Auflistung in eine ARRAY-Datei konvertiert. Ich könnte jedoch immer noch Modellmethoden für die einzelnen Instanzen in einem Iterator "each" aufrufen, was mein Problem gelöst hat. 

Dies ist meine erste StackOverflow-Antwort (Keuchen!). Ich freue mich über Ihr Feedback!

0
Eric Powell