web-dev-qa-db-fra.com

Combinez deux objets ActiveRecord :: Relation

Supposons que j'ai les deux objets suivants:

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

est-il possible de combiner les deux relations pour produire un objet ActiveRecord::Relation contenant les deux conditions?

Note: Je suis conscient que je peux enchaîner les accès pour obtenir ce comportement. Ce qui m'intéresse vraiment, c'est le cas où j'ai deux objets ActiveRecord::Relation séparés.

150

Si vous souhaitez combiner en utilisant AND (intersection), utilisez merge :

first_name_relation.merge(last_name_relation)

Si vous souhaitez combiner avec OR (union), utilisez or (disponible dans ActiveRecord 5+ ou 4.2 via où-ou backport ):

first_name_relation.or(last_name_relation)
166
Andrew Marshall

Les objets de relation peuvent être convertis en tableaux. Cela empêche d’utiliser toutes les méthodes ActiveRecord par la suite, mais ce n’était pas nécessaire. J'ai fait ça:

name_relation = first_name_relation + last_name_relation

Ruby 1.9, Rails 3.2

35
thatmiddleway

merge ne fonctionne pas comme OR. C'est simplement une intersection (AND

J'ai eu du mal à combiner ce problème avec les objets ActiveRecord :: Relation et je n'ai trouvé aucune solution efficace pour moi. 

Au lieu de chercher la bonne méthode pour créer une union à partir de ces deux ensembles, je me suis concentré sur l’algèbre des ensembles. Vous pouvez le faire de différentes manières en utilisant la loi de De Morgan

ActiveRecord fournit une méthode de fusion (AND) et vous pouvez également utiliser not method ou none_of (NOT). 

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

Vous avez ici (A u B) '= A' ^ B '

MISE À JOUR: La solution ci-dessus est bonne pour les cas plus complexes. Dans votre cas, cela suffira:

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

J'ai pu accomplir cela, même dans de nombreuses situations étranges, en utilisant le Arel de Rails.

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

Ceci fusionne les deux relations ActiveRecord en utilisant le ou le d'Arel.


La fusion, comme suggéré ici, n'a pas fonctionné pour moi. Il a supprimé le deuxième ensemble d'objets relationnels des résultats.

14
6ft Dan

Il existe une gemme appelée active_record_union Qui pourrait être ce que vous cherchez 

Voici des exemples d'utilisation:

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

Si vous avez un tableau de relations activerecord et souhaitez les fusionner, vous pouvez le faire 

array.inject(:merge)
6
mr.musicman

Voici comment je l'ai "géré" si vous utilisez pluck pour obtenir un identifiant pour chacun des enregistrements, joignez les tableaux ensemble, puis effectuez une requête pour ces identifiants joints:

  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 le force:

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
Richard Grossman

Je suis tombé sur cette question plusieurs fois et les réponses généralement acceptées n'ont jamais vraiment fonctionné pour moi. 

La solution proposée dans la réponse acceptée semblait fonctionner le mieux: 

first_name_relation.or(last_name_relation)

Mais je ne pourrais jamais obtenir que cela fonctionne et recevrais toujours une erreur 'méthode indéfinie' pour 'ou'. 

Ma solution:

combined_relation = first_name_relation + (last_name_relation - first_name_relation)

Cela me donne essentiellement les résultats d'une union entre les requêtes d'origine et les objets de relation sont toujours les objets ActiveRecord corrects. 

Ceci est similaire à la réponse fournie par @thatmiddleway, bien que je devais ajouter la soustraction pour obtenir une collection d'objets DISTINCT. Cela convertit la collection combinée en un tableau, mais je pourrais toujours appeler des méthodes de modèle sur les instances individuelles dans un itérateur "chaque" à mon avis, ce qui a résolu mon problème. 

Ceci est ma première réponse StackOverflow (halètement!), Je me réjouis donc de vos commentaires!

0
Eric Powell