ruby on rails - code - Combine dos objetos ActiveRecord:: Relation
rails code (9)
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 en el que tengo dos objetos ActiveRecord::Relation
separados.
Así es como lo "manejé" si usas desplumar para obtener un identificador para cada uno de los registros, unir los arreglos y finalmente hacer una consulta para esos identificadores 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)
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
Hay una gema llamada active_record_union que podría ser lo que estás buscando.
Su uso de ejemplo 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)
He tropezado con este tema varias 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 podría hacer que esto funcione y siempre recibiría un error de ''método indefinido'' para ''o''.
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 objetos correctos de ActiveRecord.
Esto es similar a la respuesta provista por @thatmiddleway, aunque necesitaba agregar la resta para obtener una colección DISTINCT de objetos. Esto convierte la colección combinada en un ARRAY, pero aún podía llamar a los métodos del modelo en las instancias individuales en un iterador ''cada'' en mi vista, lo que resolvió mi problema.
Esta es mi primera respuesta de (¡jadeo!), ¡Así que agradezco sus comentarios!
Los objetos de relación se pueden convertir a matrices. Esto niega el poder usar cualquier método ActiveRecord en ellos después, pero no era necesario. Hice esto:
name_relation = first_name_relation + last_name_relation
Ruby 1.9, rieles 3.2
Pude lograr esto, incluso en muchas situaciones raras, al usar el Arel incorporado de Rails.
User.where(
User.arel_table[:first_name].eq(''Tobias'').or(
User.arel_table[:last_name].eq(''Fünke'')
)
)
Esto combina ambas relaciones de ActiveRecord usando Arel''s o .
Merge, como se sugirió aquí, no funcionó para mí. Se cayó el segundo conjunto de objetos de relación de los resultados.
Si desea combinar el uso de AND
(intersección), use merge
:
first_name_relation.merge(last_name_relation)
Si desea combinar el uso de OR
(unión), use or
(disponible en ActiveRecord 5+, o en 4.2 vía where-or backport ):
first_name_relation.or(last_name_relation)
Si tiene una matriz de relaciones activas de registro y desea fusionarlas todas, puede hacer
array.inject(:merge)
merge
realmente no funciona como OR
. Es simplemente intersección ( AND
)
Luché con este problema para combinar los objetos de 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 otra manera usando la ley de De Morgan
ActiveRecord proporciona el método de combinación (AND) y también puede usar el método no o none_of (NOT).
search.where.none_of(search.where.not(id: ids_to_exclude).merge(search.where.not("title ILIKE ?", "%#{query}%")))
Usted tiene aquí (A u B) ''= A'' ^ B ''
ACTUALIZACIÓN: la solución anterior es válida para casos más complejos. En tu caso, algo así será suficiente:
User.where(''first_name LIKE ? OR last_name LIKE ?'', ''Tobias'', ''Fünke'')