rails inner includes active ruby-on-rails ruby activerecord preloading

ruby on rails - inner - La precarga tiene muchas asociaciones con condiciones dinámicas



preload rails (4)

He mencionado en algunos casos que includes no selecciona correctamente el método de carga ansioso. Hay una explicación de cómo funciona este método http://blog.arkency.com/2013/12/rails4-preloading/ . Puedes llamar directamente a eager_load(:events) y creo que cargará tus objetos AR sin n + 1 problema.

Tengo un modelo de lugar y un modelo de evento. Los lugares pueden tener eventos que tienen lugar en una fecha específica .

¿Cómo puedo configurar mis asociaciones y buscadores para cargar todos los lugares, incluyendo (carga ansiosa) sus eventos en una fecha específica sin un problema de consulta N + 1?

Lo que he intentado:

class Place has_many :events end Place.all.preload(:events).where("events.start_date > ''#{time_in_the_future}''") #ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR: missing FROM-clause entry for table "events". Place.all.includes(:events).where("events.start_date > ''#{time_in_the_future}''").references(:event) # only loads places that have an event at the specific date and not all places including their events (if there are any events).

Conseguí una asociación que hace lo que quiero pero no es dinámica (no acepta parámetros)

class Place has_many :events, -> {where("events.start_date > ''#{Time.now}''")} end Place.all.preload(:events) # perfect: executes two queries: One to get all ''places'' and one to get all ''events'' that belong to the places and merges the ''events'' into the ''place'' objects. # But I can''t pass time as a parameter, so time is always Time.now (as specified in the has_many association). # Place.all.preload(:events).where(xyz) gives wrong results like the examples above.

El problema para mí es que no puedo encontrar una forma de precargar / carga ansiosa con condiciones dinámicas. Debido a que preload e includes esperan el nombre de la asociación como parámetro y no pueden refinarse con parámetros. Al menos no encontré la manera de hacer esto.


Según tengo entendido, quiere buscar todos los lugares que tienen al menos un evento que satisfaga alguna condición, pero los lugares deben buscarse con todos los eventos, incluso aquellos que no cumplan con la condición. No puede figurar que esto está fuera con una simple consulta, pero si va a usar suquery, entonces se hará el problema. Aquí está la solución:

Place.includes(:events).where(id: Place.joins(:events).where("events.start_date > ''#{time_in_the_future}''")).references(:events)

Se creará una consulta compleja, pero hace las cosas bien.


Para resolver el problema de la fecha dinámica, ¿ha considerado:

class Event < ActiveRecord::Base belongs_to :place scope :on_date, lambda {|the_date| where(start_date: the_date) } scope :on_or_after, lambda {|the_date| where(''start_date >= ?'', the_date) } end

Entonces podrías hacer esto:

@place = Place.find(params[:id]) # let''s say... @place.events.on_date(params[:chosen_date])

Puede incorporar las cargas ansiosas que otros han mencionado también.


Esta parece ser la única solución que funciona:

# 1st query: load places places = Place.all.to_a # 2nd query: load events for given places, matching the date condition events = Event.where(place: places.map(&:id)).where("start_date > ''#{time_in_the_future}''") events_by_place_id = events.group_by(&:place_id) # 3: manually set the association places.each do |place| events = events_by_place_id[place.id] || [] association = place.association(:events) association.loaded! association.target.concat(events) events.each { |event| association.set_inverse_instance(event) } end

Es un poco raro, pero es bastante fácil adaptarse a cualquier situación en la que desee cargar una asociación usando una consulta separada y luego adjuntarla a un objeto existente.

Todo el crédito va a https://mrbrdo.wordpress.com/2013/09/25/manually-preloading-associations-in-rails-using-custom-scopessql/