Search code examples
ruby-on-rails-3activerecordruby-on-rails-3.1associations

writing associations for a recursive relationship


a little help if you will.

I would like create a recusive relationsip with my 'product' class. Where I can have one product be the parent product for other products. My questions is how do I create this relationship within my model?

would this be proper?

has_many :products
belongs_to :product

Solution

  • There is logically nothing wrong with your proposed solution. However, you probably need to do something like this:

    belongs_to :parent, class_name: "Product", foreign_key: "parent_id"
    has_many :children, class_name: "Product", foreign_key: "parent_id"
    

    You are basically storing a "tree", with one product at the top and branches of child products underneath, potentially many levels deep.

    The strategy in your example is known as an Adjacency List. It is easy to find the direct parent and direct descendants of any given record. However, getting ALL the descendants (including descendants of descendants) can be difficult, and gets more difficult the deeper the tree is.

    An alternative often used is a Nested Set, where each record stores information about the object to the "left" and "right" of it. This allows you to build a tree, no matter how big, pretty quickly (or at least efficiently, in a single query). However, it is more complicated and inserting a record usually means having to recalculate and update all records to the right of the record you inserted.

    A third option (and arguably a sort of middle-ground) is using Path Enumeration, where objects store their entire path in the tree. So, given a tree like this:

      A
     / \
    B   C
        |
        D
    

    A would have a blank path (no parent), B and C would have the path A, D would have the path A/C. Most insert operations are relatively inexpensive with this solution, and querying is pretty easy. However, its still rather complex and moving objects around in the tree can get expensive.

    There are other options, too, like Closure Tables.

    If a simple Adjacency List works for your needs then go with that. It is definitely the simplest and easiest to understand. There are gems that implement nested sets, and maybe for some other tree patterns too, so you don't have to do all the heavy lifting yourself if you go that route.