Activerecord association design advice

Hi guys. So I have a few models: Concept, Entity, Dimension, Rating, User and Fact

1) Concepts can have many dimensions and many entities and many facts. 2) Concepts may share dimensions, but not entities or facts (so the Concept universities can share the Dimension quality but not the Fact num_students. 3) A rating is determined by allowing a user to rate each dimension defined over the concept the entity belongs to.

I'm trying to design the relationships on paper before starting, and I thought I'd run it by you guys for suggestions, improvements, advice, etc (I'm quite new to rails and ruby).

def Dimension   has_and_belongs_to_many :concepts   # for the ratings table   has_many :ratings   has_many :entities, :through => :ratings   has_many :users, :through => :ratings def Fact def Entity   # for the ratings table   has_many :ratings   has_many :dimensions, :through => :ratings   has_many :users, :through => :ratings def Concept   has_and_belongs_to_many :dimensions   has_many :entities, :dependent => :destroy   has_many :facts, :dependent => :destroy def User   # for the ratings table   has_many :ratings   has_many :dimensions, :through => :ratings   has_many :entities, :through => ratings def Rating   # the ratings table also has a value field when a user rates an entity's dimension   belongs_to :dimensions   belongs_to :users   belongs_to :entities

Thanks in advance

To me it looks like you are doing way too much abstraction here. With model names like Concept, Entity and Dimension it is going to be extremly hard for anybody else to wrap their heads around what exactly you are trying to design. At least I can't do it, and hence I cant really give any advice either.

Hi, ok if you replace Concept with Vehicle (ala tank, car, truck, plane, etc), Entity with Model (Z30, 911 turbo, F16, etc), Dimension with SubjectiveDimension (quality, comfort, etc) and Fact with FactualDimension (price, model_year, max_speed, etc) would it make more sense?

  1. Vehicles have many models, subjective_dimensions and factual_dimensions

  2. Vehicles may share subjective_dimensions but not factual_dimensions or or models

  3. A rating is established by allowing users to rate all subjective_dimensions of the vehicle that the model belongs to

So, I could rate an F16 by rating dimensions such as comfort_level, sexiness, quality_of_landing and then rate a Porche 911 by rating dimensions such as comfort_level, sexiness and steering_response. Both of them (the F16 and Porche) will have a factual_dimension called price. These dimensions are defined over all objects inside a vehicle. The subjective_dimensions can be shared between different entities (such as sexiness). They can be shared because they are subjective and relative differences don’t matter. With factual_dimensions, what is considered a vehicle is not the same as a cheap car, that’s why they cannot be shared over vehicles.

So, changing the model files:

def User

has_many :ratings

has_many :subjective_dimensions, :through => :ratings

has_many :models, :through => :ratings

def Vehicle

has_and_belongs_to_many :subjective_dimensions

has_many :factual_dimensions, :dependent => :destroy

has_many :models, :dependent => :destroy

def Model

belongs_to :vehicles

has_many :ratings

has_many :subjective_dimensions, :through => :ratings

has_many :users, :through => :ratings

def SubjectiveDimension

has_and_belongs_to_many :vehicles

has_many :ratings

has_many :users, :through => :ratings

has_many :models, :through => :ratings

def FactualDimension

belongs_to :vehicles

def Rating

belongs_to :user

belongs_to :model

belongs_to :subjective_dimension

I hope that makes things a little bit clearer? There are a few other details which I would like to get feedback on as well, but I’ll leave that for after the above is taken care of.

Hi, ok if you replace (...) would it make more sense?

Absolutly! For a while there I was wondering if you were setting out to program the matrix.

Abstracting an objects attributes into it's own table with a one-to- many (or many-to-many) association between the object and attributes should be the result of extreme requirement of flexibility an DRYness. There are alternatives that are also DRY and flexible, but easier to implement.

Are you familiar with single table inheritance? To me this looks like a case where it could be a good solution, if the number of vehicle types are fairly limited. When you have several entities that are almost the same, but not quite (I.E car, plane, boat), using single table inheritance is often a great way to reduce repetition. This will enable you to put all the attributes in one table, and decide which attributes a vehicle type should use through it's type.

class Vehicle < ActiveRecord::Base   has_many :models   has_many :ratings end

Class Car < Vehicle

Class Plane < Vehicle

Class Model   belongs_to :vehicle

What I am suggesting is that you create a super-class called Vehicle, that has sub-classes for each vehicle type (car, plane, etc). Shared logic is put in the super-class, individual logic is put in the sub- class.

I assume subjective dimensions are always the result of a user rating a vehicle? What to do with rating and subjective dimensions depends on what you do with Vehicle and factual dimesions. If you decide to use single table inheritance for the latter then it makes sense to use it for the former as well. You can remove the subjective dimesions table, and instead have subjective dimensions as attributes on the ratings table, and have sub-classes of rating for each sub-class of vehicle so that you are able to decide which subjective dimensions should come into play for each vehicle type.

Looking at the design you suggest I can see that you are trying to make a system that is 100% flexible by being able to decide exactly what kind of attributes each individual object has. I myself have never tried to implement a design where I abstracted the attributes into its own table, having a one-to-many association between the object and its attributes, so I don't have any first hand experience with this. I dont imagine it will be easy. I imagine many challenges will arise from the fact that attributes can be of different types (string, integer, boolean, float, etc). If you dont need to any searching or reporting on the attributes then they could all be stored as strings, however if you need these things then you need their types, and implementing the searching and reporting will be very complex. It is also hard to imagine a system that needs to be this flexible in data storage without also having complex searching and reporting requirements.

Heh, not the matrix. What I am programming though is a ratable ontology, so the initial email with Concept, Entity and Dimension is the real code and I’m afraid flexibility is one of my main concerns because the number of vehicles, models and dimensions are completely unknown. So, single table inheritance on the vehicles is out of the question for the final application, unless of course there is a way to define models at run time, maybe every time a user defines a new vehicle I can call the ruby generator or something and have it create a class and corresponding tables. That may be overkill though. Is there any examples of web applications programming themselves? Now that sounds like some fun.

Anyway, the end goal is the ratable ontology (thesis related). For the prototype I’m developing though, I could use inheritance. The prototype uses 2 parent concepts and a few child ones (Drink and Food would be the parent concepts, and the child concepts are: Cocktail, Beer, Wine, NonAlcoholic for the former and Starter, Main and Dessert for the latter). So single table inheritance sounds good here.

Subjective dimensions are a result of a user deciding what gives an object value. So a user would go into the system and decide that, “ooh, I think the rate at which a window can be lowered and raised makes a difference for me when rating a car”, so then the user will define a new subjective dimension: window_raise_speed. The application will then attach that new subjective dimension to all the cars and allow all users of the system from that point on to rate a car over window_raise_speed.

The thing about subjective dimensions is that they are all ordinal values, so they are all represented by floats. The actual values don’t mean anything, but what they represent do (max_value means window_raise_speed is ‘perfect’, or ‘couldn’t ask for more’, hence the subjectivity). I see your point when it comes to factual dimensions though, because they are not all ordinal and could be of any type.

Really appreciate the feedback.