STI versus Composition...or the headaches of one big table

Guys,

I have a relationship between model classes that I believe is best
represented by inheritance, but the likelihood that things will change
often is driving us to composition instead.

So, I have a class called Autos, and subclasses called Suvs, Minis,
Mids, Sports, for example.

We have been requested to avoid the STI approach to this, because of
fear of a quickly growing table with many empty fields, and the fear of
having to add or delete properties of one of the above categories and
having to process every row in the entire table.

I’m trying to get my head around the best way to do this. I’d like to
break it out via composition, but having a Autos table with an suv_id,
mini_id, mid_id, and sports_id (for all possible combos) seems like
really bad design.

I suppose I could have the Autos table have a sub_type_id and sub_type
column, and somehow use this to map. However, I’m unsure how to go about
this…composed_of doesn’t seem to be designed with this situation in
mind.

Any insight or suggestions you may provide will be very welcome.

Thanks!
B.A.

B.A. Baracus: I thought you weren’t crazy no more?
Murdock: Only on paper.

Hi !

17 Jun 2006 03:45:22 -0000, BA Baracus
[email protected]:

I suppose I could have the Autos table have a sub_type_id and sub_type
column, and somehow use this to map. However, I’m unsure how to go about
this…composed_of doesn’t seem to be designed with this situation in mind.

If you used this approach, you would be using polymorphic associations:

class CreateAuto < AR::Migration
def self.up
create_table :autos do |t|
t.column :subclass_id, :integer
t.column :subclass_type, :string
end
end
end

class Auto < AR::Base
belongs_to :subclass, :polymorphic => true
end

Then, autos.subclass_id + autos.subclass_type identify the table/PK of
the relationship. See
http://wiki.rubyonrails.com/rails/pages/UnderstandingPolymorphicAssociations
for some documentation.

Hope that helps !

17 Jun 2006 05:53:50 -0000, BA Baracus
[email protected]:

Perfect…thanks! Had no idea Rails supported this! Is this a 1.1 feature?

Yes.

Bye !

On Saturday, June 17, 2006, at 12:00 AM, Francois B. wrote:

Hi !

17 Jun 2006 03:45:22 -0000, BA Baracus [email protected]:

I suppose I could have the Autos table have a sub_type_id and sub_type
column, and somehow use this to map. However, I’m unsure how to go about
this…composed_of doesn’t seem to be designed with this situation
in mind.

If you used this approach, you would be using polymorphic associations:

Perfect…thanks! Had no idea Rails supported this! Is this a 1.1
feature?

On Saturday, June 17, 2006, at 3:45 AM, BA Baracus wrote:

fear of a quickly growing table with many empty fields, and the fear of
this…composed_of doesn’t seem to be designed with this situation in mind.


Rails mailing list
[email protected]
http://lists.rubyonrails.org/mailman/listinfo/rails

I wouldn’t write off the STI approach. Updating the table with a
migration is pretty painless and can also be used to populate the
relevant fields. I don’t think there will really be that much of a
performance problem.

This sounds a bit like premature optimization to me. If you are really
concerned about it, set up a test environment to simulate stress
conditions and do some benchmarking before you try to fit a square peg
into a round hole.

_Kevin

On Sat, Jun 17, 2006 at 03:45:22AM -0000, BA Baracus wrote:
[…]
} We have been requested to avoid the STI approach to this, because of
} fear of a quickly growing table with many empty fields, and the fear
of
} having to add or delete properties of one of the above categories and
} having to process every row in the entire table.
[…]
} Any insight or suggestions you may provide will be very welcome.

If your problem fits the following criteria, read on:

  1. The subclass-specific fields are information about the object itself
    and
    has no relationship to other objects.

  2. The subclass-specific fields will not need to be queried (i.e.
    filtered,
    grouped, or aggregated), just read and written.

  3. The table in question will never need to be accessed by a non-Rails
    application.

If all those criteria apply, you can use a serialized column. The column
should be declared text in the database, and the model should look
something like this (assuming the column is named serial_col and the
table
is Foos):

class Foo < ActiveRecord::Base
serialize :serial_col

def self.serial_field(*fields)
fields.each { |field|
field = field.to_sym
define_method(field) { serial_col[field] }
define_method("#{field}=") { |val| serial_col[field] = val }
#define_method("#{field}?") #fill in as desired
}
end

def serial_col
self[:serial_col] ||= {}
end
end

In your subclasses you would created pseudo-fields:

class Bar < Foo
serial_field :baz
end

} Thanks!
} B.A.
–Greg