Active Record Relations and Nesting

Hello,
  I am looking to create a rails app that will model and display
related nodes. I am just beginning to dive into active record and I
was wondering if the following pesudo code would work?

class NodeLink < ActiveRecord::Base
  belongs_to :node # foreign key - node_id (recorded as
primary_node_id)
  belongs_to :node # foreign key - node_id (recorded as
secondary_node_id)
end

class Node < ActiveRecord::Base
  has_many :nodes, :through => :node_links
end

In essence this would create something that looks like the following.
      [Node One]
      > >
      > >
[Node Two] [Node Three]

[Node Four]

Where the node links are represented by the lines and the nodes
themselves are enclosed in brackets. And this pattern could be
continued indefinite through different variations of nodes and links.

Any help or advice you could give would be great.
Thanks,
Devin Morin

Hello,
I am looking to create a rails app that will model and display
related nodes. I am just beginning to dive into active record and I
was wondering if the following pesudo code would work?

class NodeLink < ActiveRecord::Base
belongs_to :node # foreign key - node_id (recorded as
primary_node_id)
belongs_to :node # foreign key - node_id (recorded as
secondary_node_id)
end

You'll need to specify different names for these two associations; a
working declaration might look like:

class NodeLink < ActiveRecord::Base
  belongs_to :primary_node, :class_name => 'Node'
  belongs_to :secondary_node, :class_name => 'Node'
end

class Node < ActiveRecord::Base
has_many :nodes, :through => :node_links
end

This gets messier:

class Node < ActiveRecord::Base
  has_many :primary_links, :foreign_key =>
'primary_node_id', :class_name => 'NodeLink'
  has_many :secondary_links, :foreign_key =>
'secondary_node_id', :class_name => 'NodeLink'
  has_many :primary_nodes, :through => :primary_links, :source
=> :secondary_node
  has_many :secondary_nodes, :through => :secondary_links, :source
=> :primary_node
end

This offers some additional detail:

http://stackoverflow.com/questions/578084/how-to-implement-undirected-graph-in-ruby-on-rails

Note that it's going to be fairly complicated to deal with "all nodes
that are connected to this one" unless you denormalize somewhat and
make *two* NodeLinks for each edge. You may want to consider if you
*really* need a completely undirected graph, or if there's additional
structure (tree-like behavior, for instance) that you can simplify
things with.

If this is a central concern to your app, you may want to look into
the specialized "graph databases" that are now available.

--Matt Jones

Note that it's going to be fairly complicated to deal with "all nodes
that are connected to this one" unless you denormalize somewhat and
make *two* NodeLinks for each edge. You may want to consider if you
*really* need a completely undirected graph, or if there's additional
structure (tree-like behavior, for instance) that you can simplify
things with.

If this is a central concern to your app, you may want to look into
the specialized "graph databases" that are now available.

--Matt Jones

This will be a central concern of my app, is there any resources I can
use to study undirected graphs? And I feel that the denormalization
would be an accepted way of dealing with the issue. From what I
understand this would entail having a table with

Whoops, message got cut off. From what I can guess this
denormalization just basically means you store the relations between
the nodes in a table to save on compute time when performing a lookup
on the Db?
Regards,
Devin Morin

Devin:

Unsolicited, but here's a pair of classes I've used for representing
"vertices" (nodes) and "edges" (node links). You could conceptually
"Vertex".gsub("NodeLink") and "Edge".gsub("Node") and get 80% of where
you want to get. The useful bits:

- the belongs_to clauses in Edge implement the kind of thing you're
describing
- the before_destroy clause in Vertex takes the place of dependent =>
destroy
- the edges method in Vertex finds both "left" and "right" links.

Also, this is a self-contained sample useful for testing, since it
includes the up() and down() methods to create and destroy the tables
while you're trying things out. I use this pattern a lot.

The example does NOT check for circular dependencies and such (since
edges and vertices may describe circular graphs), but you might find it
helpful.

class Edge < ActiveRecord::Base
  belongs_to :vertex_a, :class_name => 'Vertex', :foreign_key =>
'vertex_a_id'
  belongs_to :vertex_b, :class_name => 'Vertex', :foreign_key =>
'vertex_b_id'

  def self.up()
    ActiveRecord::Schema.define do
      create_table "edges", :force => true do |t|
        t.integer "vertex_a_id"
        t.integer "vertex_b_id"
      end
    end
  end

  def self.down()
    ActiveRecord::Schema.define do
      drop_table :edges
    end
  end

end

class Vertex < ActiveRecord::Base
  before_destroy do |record|
    Edge.destroy_all "vertex_a_id = #{record.id}"
    Edge.destroy_all "vertex_b_id = #{record.id}"
  end

  # has_many :vertices will not work in this case, so here's
  # how we find all edges asociated with this vertex...
  def edges
    Edge.find_all_by_vertex_a_id(self.id) +
Edge.find_all_by_vertex_b_id(self.id)
  end

  has_many :vertices

  def self.up()
    ActiveRecord::Schema.define do
      create_table "vertices", :force => true do |t|
        t.float "x"
        t.float "y"
        t.float "z"
      end
    end
  end

  def self.down()
    ActiveRecord::Schema.define do
      drop_table :vertices
    end
  end

end

# Testing...

def setup
  Edge.up
  Vertex.up
  v0 = Vertex.create(:x => 0.0, :y => 0.0, :z => 0.0)
  v1 = Vertex.create(:x => 1.0, :y => 1.0, :z => 1.0)
  e0 = Edge.create(:vertex_a => v0, :vertex_b => v1)
  [v0, v1, e0]
end

def teardown
  Edge.down
  Vertex.down
end