209 votes

Combiner deux objets ActiveRecord::Relation

Supposons que je possède 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 en produire une seule ? ActiveRecord::Relation contenant les deux conditions ?

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

0 votes

262voto

Andrew Marshall Points 43955

Si vous voulez combiner l'utilisation de AND (intersection), utilisez merge :

first_name_relation.merge(last_name_relation)

Si vous voulez combiner l'utilisation de OR (syndicat), utilisez or † :

first_name_relation.or(last_name_relation)

† Uniquement dans ActiveRecord 5+ ; pour 4.2, installez l'option où-ou backport.

0 votes

Et si je veux que le résultat soit un fichier de type ActiveRecord::Relation type ?

0 votes

@NewAlexandria merge renvoie un ActiveRecord::Relation

0 votes

42voto

thatmiddleway Points 1321

Les objets relationnels peuvent être convertis en tableaux. Cela empêche d'utiliser les méthodes ActiveRecord sur ces objets par la suite, mais je n'en ai pas eu besoin. J'ai fait ceci :

name_relation = first_name_relation + last_name_relation

Ruby 1.9, rails 3.2

0 votes

Mise à jour : dommage que cela ne fusionne pas par date

82 votes

Il ne se comporte pas comme un tableau, il convertit votre relation en un tableau. Vous ne pouvez donc plus utiliser les méthodes ActiveRecord comme .where() ...

2 votes

Si vous ne tenez pas à ce que le type de retour soit une relation, vous pouvez combiner plusieurs résultats avec l'opérateur "first_name_relation | last_name_relation". L'opérateur "|" fonctionne également sur plusieurs relations. La valeur du résultat est un tableau.

24voto

merge ne fonctionne pas vraiment comme OR . Il s'agit simplement d'une intersection ( AND )

J'ai lutté avec ce problème pour combiner deux objets ActiveRecord::Relation en un seul et je n'ai pas trouvé de solution qui fonctionne 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 manière différente en utilisant La loi de Morgan

ActiveRecord fournit la méthode merge (AND) et vous pouvez également utiliser la méthode not 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'

UPDATE : La solution ci-dessus est bonne pour les cas plus complexes. Dans votre cas, une solution de ce type sera suffisante :

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

17voto

6ft Dan Points 285

J'ai pu y parvenir, même dans de nombreuses situations bizarres, en utilisant la fonction intégrée de Rails, à savoir Arel .

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

Cela permet de fusionner les deux relations ActiveRecord en utilisant la méthode d'Arel. o .


La fusion, telle qu'elle a été suggérée ici, n'a pas fonctionné pour moi. Le deuxième ensemble d'objets de relation a été supprimé des résultats.

15voto

echo Points 850

Il existe une pierre précieuse appelée union_enregistrement_actif qui pourrait correspondre à ce que vous recherchez.

Ses exemples d'utilisation sont les suivants :

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)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X