Combine dos objetos ActiveRecord:: Relation


Supongamos que tengo los siguientes dos objetos:

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

¿Es posible combinar las dos relaciones para producir un objeto ActiveRecord::Relation que contenga ambas condiciones?

Nota: Soy consciente de que puedo encadenar los wheres para obtener este comportamiento, lo que realmente me interesa es el caso donde tengo dos objetos ActiveRecord::Relation separados.

Author: Andrew Marshall, 2012-03-03

9 answers

Si desea combinar usando AND (intersección), use merge:

first_name_relation.merge(last_name_relation)

Si desea combinar usando OR (union), use or (disponible en ActiveRecord 5+, o en 4.2 vía donde-o backport):

first_name_relation.or(last_name_relation)
 157
Author: Andrew Marshall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-02 23:44:43

Los objetos de relación se pueden convertir en matrices. Esto niega poder usar cualquier método ActiveRecord en ellos después, pero no lo necesitaba. Hice esto:

name_relation = first_name_relation + last_name_relation

Ruby 1.9, rails 3.2

 32
Author: thatmiddleway,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-09-23 16:01:02

merge en realidad no funciona como OR. Es simplemente intersección (AND)

Luché con este problema para combinar los objetos ActiveRecord::Relation en uno y no encontré ninguna solución de trabajo para mí.

En lugar de buscar el método correcto para crear una unión a partir de estos dos conjuntos, me centré en el álgebra de conjuntos. Puedes hacerlo de manera diferente usando La ley de De Morgan

ActiveRecord proporciona merge method (AND) y también puede usar not method o none_of (NOT).

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

Tienes aquí (A u B)' = A' ^ B'

ACTUALIZAR: La solución anterior es buena para casos más complejos. En su caso smth como que será suficiente:

User.where('first_name LIKE ? OR last_name LIKE ?', 'Tobias', 'Fünke')
 18
Author: Tomasz Jaśkiewicz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-07 10:50:25

He sido capaz de lograr esto, incluso en muchas situaciones extrañas, mediante el uso de Rails' built-in Arel.

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

Esto fusiona ambas relaciones ActiveRecord usando o de Arel.


La fusión, como se sugirió aquí, no funcionó para mí. Eliminó el 2do conjunto de objetos de relación de los resultados.

 14
Author: 6ft Dan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-06 04:33:10

Hay una gema llamada active_record_union eso podría ser lo que estás buscando.

Su ejemplo de usos es el siguiente:

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)
 10
Author: echo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-11 22:39:00

Así es como lo he "manejado" si usa pluck para obtener un identificador para cada uno de los registros, unir los arrays y finalmente hacer una consulta para esos id unidos:

  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
Author: daveomcd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-05 20:40:01

Si tiene una matriz de relaciones activerecord y desea fusionarlas todas, puede hacer

array.inject(:merge)
 5
Author: mr.musicman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-04 15:45:49

Fuerza bruta:

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
Author: Richard Grossman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-21 00:31:19

Me he topado con este tema un puñado de veces y las respuestas generalmente aceptadas nunca funcionaron para mí.

La solución propuesta en la respuesta aceptada parecía que funcionaría mejor:

first_name_relation.or(last_name_relation)

Pero nunca pude conseguir que esto funcione y siempre recibiría un error 'undefined method' para 'or'.

Mi solución:

combined_relation = first_name_relation + (last_name_relation - first_name_relation)

Esto me da esencialmente los resultados de una UNIÓN entre las consultas originales y los objetos de relación siguen siendo los correctos Objetos ActiveRecord.

Esto es similar a la respuesta proporcionada por @thatmiddleway, aunque necesitaba agregar la resta para obtener una colección DISTINTA de objetos. Esto convierte la colección combinada en una MATRIZ, pero todavía podría llamar a los métodos de modelo en las instancias individuales en un iterador 'cada' en mi vista, lo que resolvió mi problema.

Esta es mi primera respuesta de StackOverflow (gasp!), por lo que doy la bienvenida a sus comentarios!

 0
Author: Eric Powell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-29 16:36:19