Hello - I have a feature request/suggestion to run by the group for 
feedback.

*tl;dr* - CTI objects that are not fully populated (because they were 
retrieved by the superclass dataset) lazily load missing columns in a 
less-than-optimal way.  I'd like to fix that

*Use Case:*
We have payments, payment_credit_cards and payment_checks set up using 
class table inheritance. We convert this set of objects into an array of 
hashes and then to JSON for an API.

*Problem:*
When constructing the hash for each object, they are already of the correct 
class but only have fields from the parent class loaded.  When accessing 
each column that we want in the resulting hash a new query is performed 
(CTI uses the lazy_attributes plugin to facilitate this).  The 
tactical_eager_loading plugin does help in that each column-specific query 
loads values for all relevant objects in the dataset but if you have a 
large number of columns in a sub-table it results in a lot of additional 
queries when in theory, you should be able to load all of those values at 
once.

*Proposed Solutions:*
In my opinion, we need a way for columns on a sub-class that aren't already 
loaded to be loaded as a group instead of one by one as they are accessed.

*Option #1* - implicitly load missing CTI columns when the first one is 
accessed

This is what I implemented 
here: https://github.com/jeremyevans/sequel/pull/1055

Basically, the interface would work like this:

cc_payment = Payment.where(kind: PaymentCreditCard).first # no columns from 
payment_credit_cards loaded
exp_month = cc_payment.expiration_month # all columns from 
payment_credit_cards loaded
exp_year = cc_payment.expiration_year   # no query done as the 
`expiration_year` column was already loaded on the prior line

*Option #2* - add a method that requires the caller to explicitly load 
missing columns (but still load them as a group)

This would preserve existing behavior in all cases and expose a new method 
that could be called to load extra columns.  Something like this:

cc_payment = Payment.where(kind: PaymentCreditCard).first # no columns from 
payment_credit_cards loaded
cc_payment.load_attributes(cc_payment.class.columns - cc_payment.keys) # 
all columns from payment_credit_cards loaded
exp_month = cc_payment.expiration_month # no query done
exp_year = cc_payment.expiration_year   # no query done

*Option #3* - combination of #1 and #2 with an option in the CTI plugin 
that enables #1's behavior (otherwise, #2 is still available on demand)

Something like this:

class Payment < Sequel::Model
  plugin :class_table_inheritance, bulk_lazy_load: true, ... # enables 
Option #1's behavior
end

class SomeOtherModel < Sequel::Model
  plugin :class_table_inheritance, ... # maintains current behavior
end

On classes that don't have the "bulk_lazy_load" set to "true", you would 
use the "load_attributes" method defined in option #2.  Otherwise, your 
code could rely on option #1's implicit loading.

*My Preference:*
Obviously, since I coded up Option #1, it would be my first choice 
(although I could live with any of the above).  The reason I think #1 (or 
at minimum #3) is the best choice is that I think the caller shouldn't have 
to think about whether the object it's working with is fully loaded or not. 
 In this case, I have a PaymentCreditCard object that may or may not be 
fully populated (depending on if I used the Payment or the 
PaymentCreditCard dataset to retrieve it).  I think it would be a good 
feature for the CTI (and thus the lazy_attributes) plugins to support the 
ability to load all the necessary columns implicitly when it deems it 
necessary in the most efficient way possible.  Right now, it loads the 
columns implicitly, but in a less-than-optimal fashion (retrieving one 
column at a time from the DB).  I'm doubtful that it's obvious to those 
using the CTI plugin that it has this characteristic which is why I think 
it should always be loaded implicitly.  However, I would be fine with 
making it configurable when the plugin is enabled for a class (option #3).

There is some prior art for this exact implementation 
here: http://docs.sqlalchemy.org/en/rel_0_9/orm/loading_columns.html

*The Question:*
What option do you find most intuitive and preferable?

-- 
You received this message because you are subscribed to the Google Groups 
"sequel-talk" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/sequel-talk.
For more options, visit https://groups.google.com/d/optout.

Reply via email to