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

On Dec 22, 11:16pm, Devin M [email protected] wrote:

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:

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 J.

–Matt J.
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