Recursive self-referential many-to-many relationship

I have a 'Circuit' table, and a 'Glue' table to perform a many-to-many
join on the circuit records, such at a circuit can have many subcircuits
(its component parts) and many supercircuits (circuits which it itself
is a part of).

  has_and_belongs_to_many :subcircuits,
    :class_name => "Circuit",
    :join_table => "circuit_glue",
    :association_foreign_key => "PTR_component",
    :foreign_key => "PTR_circuit"
  has_and_belongs_to_many :supercircuits,
    :class_name => "Circuit",
    :join_table => "circuit_glue",
    :foreign_key => "PTR_component",
    :association_foreign_key => "PTR_circuit"

With this, I can do successfully things like
my_circuit.subcircuits[0].name
but if I try
my_circuit.subcircuits[0].subcircuits.any_method
... I get a NoMethodError: You have a nil object when you didn't expect
it!
The objects in the cct.subciruits array do not appear to 'full' circuit
objects in as much as they do not repsonsd to 'subcircuits'
To build a recursive list of subcircuits I find I have to resort to

self.subcircuits.each { |sc|
subcirc_array.push(Circuit.find(:all).detect { |c| c.absid.to_s ==
sc.PTR_component })}
... which seems pretty slow and laborious. Any advice on how to speed
this up, and/or get ...subciruits[n].subcircuits to work as required?
Thanks in advance.

Toby Rodwell wrote:

I have a 'Circuit' table, and a 'Glue' table to perform a many-to-many
join on the circuit records, such at a circuit can have many subcircuits
(its component parts) and many supercircuits (circuits which it itself
is a part of).

In other words, you have a hierarchical tree of circuits.

  has_and_belongs_to_many :subcircuits,
    :class_name => "Circuit",
    :join_table => "circuit_glue",
    :association_foreign_key => "PTR_component",
    :foreign_key => "PTR_circuit"
  has_and_belongs_to_many :supercircuits,
    :class_name => "Circuit",
    :join_table => "circuit_glue",
    :foreign_key => "PTR_component",
    :association_foreign_key => "PTR_circuit"

With this, I can do successfully things like
my_circuit.subcircuits[0].name
but if I try
my_circuit.subcircuits[0].subcircuits.any_method
... I get a NoMethodError: You have a nil object when you didn't expect
it!
The objects in the cct.subciruits array do not appear to 'full' circuit
objects in as much as they do not repsonsd to 'subcircuits'
To build a recursive list of subcircuits I find I have to resort to

self.subcircuits.each { |sc|
subcirc_array.push(Circuit.find(:all).detect { |c| c.absid.to_s ==
sc.PTR_component })}
... which seems pretty slow and laborious. Any advice on how to speed
this up, and/or get ...subciruits[n].subcircuits to work as required?
Thanks in advance.

You want awesome_nested_set, which will let you do that with one query.
The Glue model is unnecessary.

Best,

Marnen Laibow-Koser wrote:

You want awesome_nested_set, which will let you do that with one query.
The Glue model is unnecessary.

Thanks very much for the info. I've had a quick look at the
documentation and I see it makes use of fields :lft and :rgt -I guess I
can use aliases for these, along the lines of :lft => "my_field", as I
don't have control of the database? Also, do you know if
awesome_nested_set will be faster than what I'm doing currently, or just
more convenient?

regards

Toby Rodwell wrote:

Marnen Laibow-Koser wrote:

You want awesome_nested_set, which will let you do that with one query.
The Glue model is unnecessary.

Thanks very much for the info. I've had a quick look at the
documentation and I see it makes use of fields :lft and :rgt -I guess I
can use aliases for these, along the lines of :lft => "my_field", as I
don't have control of the database?

If you don't have control of the database, you should not be doing the
project. Really.

You may be able to use aliases, but how does this solve the basic
problem of adding fields to the DB?

Also, do you know if
awesome_nested_set will be faster than what I'm doing currently, or just
more convenient?

Probably both. As I said, the advantage of nested sets is that you can
fetch all descendants to arbitrary depth with one query.

regards

Best,

Marnen Laibow-Koser wrote:

Toby Rodwell wrote:

Marnen Laibow-Koser wrote:

You may be able to use aliases, but how does this solve the basic
problem of adding fields to the DB?

I don't add fields (or even records) to this database - I just use RoR
as way a way to get data out in a useful format. Anyway, thanks for the
pointer to 'awesome'.

Toby Rodwell wrote:

Marnen Laibow-Koser wrote:

Toby Rodwell wrote:

Marnen Laibow-Koser wrote:

You may be able to use aliases, but how does this solve the basic
problem of adding fields to the DB?

I don't add fields (or even records) to this database - I just use RoR
as way a way to get data out in a useful format. Anyway, thanks for the
pointer to 'awesome'.

You'll have to add the lft and rgt fields to the database to use nested
sets. That's the way the data structure works.

Best,

I don't think this is a valid general point. There are often
situations involving a legacy database where it is not feasible to
change the database but where RoR makes sense for additional
functionality, particularly web access.

Colin

Colin Law wrote:

Toby Rodwell wrote:
...
If you don't have control of the database, you should not be doing the
project. �Really.

I don't think this is a valid general point. There are often
situations involving a legacy database where it is not feasible to
change the database but where RoR makes sense for additional
functionality, particularly web access.

Rails can work with legacy DBs to a certain extent. It wants its DBs
set up a certain way, though, and that's where the developer needing to
have control of the DB comes in.

It's particularly necessary if the OP wants to make the transition to
nested sets, since you can't do that without storing the left and right
values.

Colin

Best,