Creating reflective has_and_belongs_to

Hi I have a question is it possible to create the reflective relation
with users and friends using has_and_belongs_to. I found this post
that is partly connected to my:
http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/dff2032301961e2d

but my issue is join table “friends_users” where I would need to
create duplication of data when the relationship is reflective: every
time someone create the friend I need to add two rows to the join
table (user, friend) and (friend, user) with just swapped ids. Is
there any better way of handling this type of relations?

Piotr J. Puczyński

Piotr J. Puczyński wrote:

Hi I have a question is it possible to create the reflective relation
with users and friends using has_and_belongs_to. I found this post
that is partly connected to my:
http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/dff2032301961e2d

but my issue is join table “friends_users” where I would need to
create duplication of data when the relationship is reflective: every
time someone create the friend I need to add two rows to the join
table (user, friend) and (friend, user) with just swapped ids. Is
there any better way of handling this type of relations?

Sure. One possibility (untested): create a join model (I’d call it
Friendship) and, in its constructor, sort the two user IDs. That way,
you don’t need both (3, 5) and (5, 3); they both normalize to (3, 5), so
that’s all you have to search for.

Piotr J. Puczyński

Best,

Marnen Laibow-Koser
http://www.marnen.org
[email protected]

Marnen Laibow-Koser wrote:

Piotr J. Puczyński wrote:

Hi I have a question is it possible to create the reflective relation
with users and friends using has_and_belongs_to. I found this post
that is partly connected to my:
http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/dff2032301961e2d

but my issue is join table “friends_users” where I would need to
create duplication of data when the relationship is reflective: every
time someone create the friend I need to add two rows to the join
table (user, friend) and (friend, user) with just swapped ids. Is
there any better way of handling this type of relations?

Sure. One possibility (untested): create a join model (I’d call it
Friendship) and, in its constructor, sort the two user IDs.

…or use before_save for this.

Best,

Marnen Laibow-Koser
http://www.marnen.org
[email protected]

Thank you for the message,
Yes it is some idea for adding and removing friendships. But I still
cannot imagine how this model will enable listing for friends of user x.
user = User.find(id)
comrades = user.friends

I think sorted model will not work in that case (it doesn’t know in
which column should it search).

Piotr J. Puczyński

Piotr Jacek Puczynski wrote:

Thank you for the message,
Yes it is some idea for adding and removing friendships. But I still
cannot imagine how this model will enable listing for friends of user x.
user = User.find(id)
comrades = user.friends

I think sorted model will not work in that case (it doesn’t know in
which column should it search).

It can search in both. The SQL gets a little more complex, but it’s
feasible.

Basically, you have a choice here.

Option 1: sorted IDs
Pro: fewer records
Con: more complex searching

Option 2: two records – (3, 5) and (5, 3)
Pro: easier searching
Con: more records, needs extra logic to prevent getting out of sync

I think I’d go with option 1 in most cases, but I’m not absolutely sure.

A Google search for “undirected graphs in SQL” may provide useful
results, including
http://techportal.ibuildings.com/2009/09/07/graphs-in-the-database-sql-meets-social-networks/
. This is, frankly, not a trivial problem. Good luck!

Piotr J. Puczyński

Best,

Marnen Laibow-Koser
http://www.marnen.org
[email protected]

OK.
I also think the first option is better. However I don’t know how to
force the model to join based on two columns in join table so that I
will be able to write simple ‘user.friends’ each time I need a friends’
list. Do you know how to hack it?

Piotr J. Puczyński

Piotr Jacek Puczynski wrote:

OK.
I also think the first option is better. However I don’t know how to
force the model to join based on two columns in join table so that I
will be able to write simple ‘user.friends’ each time I need a friends’
list. Do you know how to hack it?

See the article I pointed to. You’ll either need a union query or two
joins to the same table.

If you’re going to do this, please make sure you understand the SQL
involved. ActiveRecord exists to abstract, not to hide.

Piotr J. Puczyński

Best,

Marnen Laibow-Koser
http://www.marnen.org
[email protected]

Hi.
I was discussing the issue with one of my friends that teaches the
databases course in the university and he said that more practically is
to have this small duplication then to use slow outer join queries to
list friends. The duplication in that case will not cause lot of space
in database. It’s worth to think of that argumentation.

Piotr Jacek Puczynski