On Feb 28, 6:54 pm, John Firebaugh <[email protected]> wrote:
> I have a problem I've been banging my head against for awhile and I can't
> seem to figure out a good approach. Imagine an application that collects
> some historical data over time for a related model:
>
> class Albums < Sequel::Model
>   one_to_many :daily_sales_figures, :order => :date
> end
>
> class DailySalesFigure < Sequel::Model
>   # Includes date column and numerous statistics such as units sold, average
> price, etc.
>   many_to_one :album
> end
>
> The user wants to see a list of albums, each with a sparkline showing a
> daily sales figure for that album. For the sparkline, they want to be able
> to choose both the time period (last quarter, YTD, etc.) and the statistic
> (units sold, average price, etc.).
>
> The obvious way to implement this is as a query over albums, eager loading
> :daily_sales_figures. Here's the nut of the problem: suppose sales figures
> go back for decades, and include hundreds of statistics, so that for
> performance reasons, it would be highly advantageous to be able to filter
> the eager loaded dataset to just the chosen time period, and select only the
> single column needed for the sparkline. There doesn't seem to be an easy way
> to do this with Sequel -- #eager uses the association options (e.g.
> :conditions, :select) that were fixed at association definition time,
> whereas the user's choices change from request to request.
>
> Right now I'm doing some crazy stuff involving dynamically creating new
> association reflections at query time, but it's really really ugly. I've
> also considered constructing the join and updating association caches
> manually -- this doesn't seem ideal either, as it essentially means a
> bespoke reimplementation of #eager at every place this needs to be done (and
> I have multiple analogous models). Is there some other way to get some
> dynamism with eager loading?

If you think about what your asking for, then you really are creating
a new association per query, since you are changing the parameters
under which the association operates.  However, in your case, hard
coding each possible combination probably isn't acceptable.

If you really want to do this, it's been possible without much
trickery since the eager loader started accepting a single hash of
options.  You'll need to have a custom eager loader, but you can
reference the :self entry in the hash to get the parent dataset.  Then
you can set eager loader parameters like:

  class Album < Sequel::Model
    one_to_many
daily_sales_figures, :order=>:date, :eager_loader=>(proc do |h|
      opts = h[:self].opts[:daily_sales_figures_opts]
      DailySalesFigure.filter(opts[:filter]).select(*opts[:select])...
    end)

    def_dataset_method(:with_daily_sales_figures) do |v|
      clone(:daily_sales_figures_opts => v).eager(daily_sales_figures)
    end
  end
 
Album.with_daily_sales_figures(:filter=>foo, :select=>[:col1, :col2])

I suppose it would be possible to use datasets as hash values, as you
suggest, even though hash values are currently used for recursive/
chained eager loading.  That's obviously an easier way to get dynamic
eager loading compared to my code above.  It would require changing
the default eager loaders, and putting the dataset to use in the hash
passed to the eager loaders.  You'd also need to deal with possible
recursion when also specifying a dataset argument.  Those shouldn't be
too difficult cases to handle, though.  After 3.21.0 is released
tomorrow, give a shot a modifying the eager_load method to handle that
case using your proposed API, and see how it works in your app.  If
you find it works well, send in a patch with specs.

Thanks,
Jeremy

-- 
You received this message because you are subscribed to the Google Groups 
"sequel-talk" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/sequel-talk?hl=en.

Reply via email to