-
Notifications
You must be signed in to change notification settings - Fork 276
Neo4j v3 Declared Relationships
The following describes behavior not yet merged into Master.
This documentation describes features of the query_experiment
branch, which are nearing completing and will be merged soon. For the current (August 7, 2014) behavior in master, see the old docs referencing has_n
and has_one
.
Declare relationships in your models to create methods for getting, setting, and traversing relationships.
There are a few configurable options possible in all relationship declarations. A basic understanding of Neo4j relationships is helpful, bordering on necessary, so see http://docs.neo4j.org/chunked/stable/graphdb-neo4j-relationships.html.
Those can be:
- :in
- :out
- :both
:both
comes with the caveat that the relationship created will be outgoing but matches will omit the direction.
class Show
include Neo4j::ActiveNode
has_many :in, :bands
end
Second parameter is a symbol that sets the method and defaults: relationship type and expected object class
class Show
include Neo4j::ActiveNode
has_many :in, :bands
end
- Method is "bands"
- Relationship type is "#bands"
- Looks for constant Band These are only DEFAULTS, though. All can be overridden.
In the above example, the relationship type is "#bands". The reason it is that and not just "bands" is because it acts as a reminder that this relationship type was created by ActiveNode. In practice, the idea is that if you come across this relationship through a match, you don't have to wonder what it is or where it came from, since you will not have specified it explicitly anywhere. It signifies that it was automatically generated.
It is considered a best practice to specify a relationship type and not rely on automatic naming. Automatic naming, while helpful, runs the risk of reusing types and just generally making Cypher queries a bit cumbersome (and let's be honest, ugly) should you ever need to use them.
By default, the second parameter defines the methods to be created and tells the association to look for a class of that name. Use the model_class
option to specify a target class.
class Show
include Neo4j::ActiveNode
has_many :in, :bands
# looks for Band
has_many :in, :people_playing_music
# Uninitialized Constant PeoplePlayingMusic
has_many :in, :people_playing_music, model_class: Band
# overrides default
end
To define a polymorphic association, pass model_class
boolean false.
class User
include Neo4j::ActiveNode
has_many :out, :managed_objects, type: 'manages', model_class: false
end
class Show
include Neo4j::ActiveNode
has_many :in, :admins, model_class: User, type: 'manages'
# see the notes on "origin" below for a better way of doing this
end
class Band
include Neo4j::ActiveNode
has_many :in, :admins, model_class: User, type: 'manages'
end
By default, it bases relation type off of the symbol passed. Use type
to declare a specific type. Doing this is considered a best practice.
class Show
include Neo4j::ActiveNode
has_many :out, :people_playing_instruments, type: 'performing_bands', model_class: Band
end
Rather than declaring the full relationship on two models, you can use origin
to reference a "master" association.
class Show
include Neo4j::ActiveNode
has_many :out, :bands, type: 'performing_bands'
has_one :out, :headliner, model_class: Band, type: 'headlined_by'
end
class Band
include Neo4j::ActiveNode
has_many :in, :shows, origin: :bands
has_many :in, :headlining_shows, model_class: Show, origin: :headliner
end
The benefit of this is that changing the relationship types declared on Show will not require updates to Band. There is still a dependency -- changing the method on Show will break Band -- but this is testable and very easy to catch.
pending implementation
You can specify before and after callback methods on all declared relationships. There are some simple guidelines:
- Your callback methods must accept one parameter that represents the other node in the relationship. Use
self
for the node where the method is defined. - If your before callback explicitly returns
false
, the relationship will not be created. -
has_n relationship setters will return false if a callback explicitly returns
false
, has_one will always return the object passed into the setter, so check for failure if you need to take action in your app
class Topic
include Neo4j::ActiveNode
property :last_post, type: DateTime
has_many :in, :posts, before: :check_poster, after: :set_topic_stats
has_one :out, :poster, model_class: User
private
def check_poster(other)
return false if other.poster.nil?
end
def set_topic_stats(other)
self.poster = other.poster
self.last_post = DateTime.now
self.save
end
end
class Post
include Neo4j::ActiveNode
has_one :in, :poster, model_class: User, origin: :posts, before: :check_post_privileges, after: :notify_friends
def check_post_privileges(other)
return false if other.allowed_to_post == false
end
def notify_friends(from, to)
# call a method to notify the poster's friends of a new post
other.notify_friends(self)
end
end
# elsewhere in the app...
if !(@topic.posts << post)
#raise an error, notify someone, do something...
end
# but what if...
@post.poster = @user
# has_one callbacks do not return false, so check after setting
if @topic.poster.nil?
notify_admins_banned_user_is_being_shady
end
You can create an undeclared relationship between two Neo4j::ActiveNode
models.
from_node.create_rel("FRIENDS", to_node)
...and add properties to the relationship.
from_node.create_rel("FRIENDS", to_node, :weight=>1)
Querying an undeclared relationship is same as querying any declared relationship.
from_node.rels(dir: :outgoing, type: "FRIENDS")
to_node.rels(dir: :incoming, type: "FRIENDS")
from_node.rels(dir: :outgoing, type: "FRIENDS").each do |rel|
puts rel.start_node # from_node itself
puts rel.end_node # to_node
end
There are a few different ways to return a relationship object. If you traversing a relationship between two nodes and want to work with the relationship between them at the same time, an easy way to do that is with each_with_rel
.
student = Student.first
student.lessons(:node, :rel).each_with_rel do |node, rel|
puts n.name
puts rel[:name] #at the moment, rel props are only accessible by keys, not accessor methods
end
Note that you must assign node and relationship identifiers in your association method call.
The result is one query and one method call instead of two.
WARNING: Much of the information in this wiki is out of date. We are in the process of moving things to readthedocs
- Project Introduction
- Neo4j::ActiveNode
- Neo4j::ActiveRel
- Search and Scope
- Validation, Uniqueness, and Case Sensitivity
- Indexing VS Legacy Indexing
- Optimized Methods
- Inheritance
- Core: Nodes & Rels
- Introduction
- Persistence
- Find : Lucene
- Relationships
- Third Party Gems & extensions
- Scaffolding & Generators
- HA Cluster