views:

58

answers:

1
        Foreign Direct Investment (% GDP)   


            2000    2001    2002    2003    2004    2005    2006
Brazil    5.09    4.05    3.28    1.84    2.74    1.7     1.73      
China      3.22    3.36    3.39    2.86    2.84    3.44    2.81     
India      0.78    1.15    1.11    0.73    0.83    0.94    2.13     
Mexico    2.87    4.43    3.37    2.35    3.11    2.57    2.01

I have 2 ideas for my Ruby on Rails application:

1) Have 3 models:

  • MetricType will contain the type of metric (FDI) and will have many DataPoint objects
  • DataPoint will have a value and year and will belong to one Country object and one MetricType object
  • A Country object will also have many DataPoint objects (representing a row)

2) Less normalized but maybe more efficient for handling lots of data

  • MetricType will contain the type of metric (FDI) and will have many DataSet objects
  • DataSet will belong to one MetricType and one Country object
  • DataSet will have an array of hashes to represent values - [{"year" => "value"},{"year" => "value"},...]

Are these both good methods for abstracting the data? Which would you use or would you propose an alternate method?

Thanks!

+1  A: 

It depends on how dynamic you want your system to be. You could do it simply with one class (and db table) per metric type and associate them via Country.has_many :foreign_direct_investment_metrics, etc (easier).

Or you could go more dynamic, I would use Single Table Inheritance on Metrics, like this:

class Country < ActiveRecord::Base
  has_many :data_points, :class_name => 'Metric'
  has_many :foreign_direct_investments, :class_name => 'ForeignDirectInvestment'
end

class Metric < ActiveRecord::Base
  belongs_to :country
end

class ForeignDirectInvestment < Metric
end

class OtherMetricType < Metric
end

# c = Country.new(:name => 'Brazil')
# c.data_points << ForeignDirectInvestment.new(:year => 2000, :value => 5.09)
# c.save

And your db schema:

  create_table "countries", :force => true do |t|
    t.string   "name"
    t.datetime "created_at"
    t.datetime "updated_at"
  end   

  create_table "metrics", :force => true do |t|
    t.integer  "country_id"
    t.string   "type"
    t.integer  "year"
    t.float    "value"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

This lets you add methods to ForeignDirectInvestment while keeping you flexible for recording more metrics. ActiveRecord's STI does have caveats, though, such as all your keys for all your models have to go on the base metrics table. There's a good intro to STI here: http://juixe.com/techknow/index.php/2006/06/03/rails-single-table-inheritance/

You might also want to consider a document db like MongoDb. MongoMapper is a simple data mapper for Mongo and Rails, and it makes solving these kinds of problems easier too. See http://mongomapper.com/

joshsz
This is great information. Confirms and elaborates on my "hunch." One other important thing is that each metric type will have a weight. I could add a weight column to the metrics table and store it there but because all metrics of one type will have the same weight, it seems redundant. This is why I suggested a MetricType object to contain any information about the MetricType. I think this will break STI which relies on that type field (and not type_id)...correct?
You could simply define the weight on the appropriate child class, like `class ForeignDirectInvestment < Metric def weight 5 end end`
joshsz
weight needs to be configurable...not constant
You could have a separate model MetricWeight that is keyed to the type column on metrics. It's a little non-standard, but it would work.
joshsz