Reputation: 4721
What would be the proper way to create a relationship between User
s that have a bidirectional relationship?
Say I have:
class User
include Neo4j::ActiveNode
property :name, type: String
property :created_at, type: DateTime
property :updated_at, type: DateTime
has_many :both, :friends, model_class: 'User', type: 'connection' unique: true
has_many :in, :followers, model_class: 'User', type: 'connection', unique: true
has_many :out, :following, model_class: 'User', type: 'connection', unique: true
end
And then,
users = [
User.create(name: 'Foo'),
User.create(name: 'Bar'),
User.create(name: 'Baz'),
]
Would this be the appropriate way to do it? It seems wildly inefficient:
users.each do |user|
user.friends << users.reject { |u| u == user }
end
Upvotes: 2
Views: 283
Reputation: 5472
If you want to ensure that user.friends
returns the other two users, I'd actually do this:
users.each do |user|
other_users = users.reject { |u| u == user || user.friends.include?(u) }
user.friends << other_users
end
Now that is wildly inefficient! It creates a new array and performs an additional lookup against the database for each user. (FWIW, the include?
lookup is very fast. You can also remove that reject
and iterate through users
once again to speed it up.) Still, it's necessary to accomplish this because otherwise, you'll create duplicate relationships.
Setting unique: true
in your association is only going to save you in one direction. Queries against has_many :both
associations use a direction-agnostic Cypher MATCH
but CREATE
actions always need direction, so they go from the node on the left to the node on the right. user1.friends << user2
and user2.friends << user1
will create two relationships between the two nodes, one in each direction.
EDIT
As you pointed out in your own comment to an issue on Neo4j.rb, using user.friends = [other_friends]
will always clear out all existing relationships and prevent duplicates. It's a really cool way of solving the problem.
Upvotes: 2