Relationship is a module in the neo4j gem which wraps relationships. Relationship objects share most of their behavior with Node objects. Relationship is purely optional and offers advanced functionality for complex relationships.

When to Use?

It is not always necessary to use Relationship models but if you have the need for validation, callback, or working with properties on unpersisted relationships, it is the solution.

Note that in Neo4j it isn’t possible to access relationships except by first accessing a node. Thus Relationship doesn’t implement a uuid property like Node.


Relationship model definitions have three requirements:

  • include ActiveGraph::Relationship
  • Call from_class with a symbol/string referring to an Node model or :any
  • Call to_class with a symbol/string referring to an Node model or :any

See the note on from/to at the end of this page for additional information.

# app/models/enrolled_in.rb
class EnrolledIn
  include ActiveGraph::Relationship
  before_save :do_this

  from_class :Student
  to_class   :Lesson
  # `type` can be specified, but it is assumed from the model name
  # In this case, without `type`, 'ENROLLED_IN' would be assumed
  # If you wanted to specify something else:
  # type 'ENROLLED'

  property :since, type: Integer
  property :grade, type: Integer
  property :notes

  validates_presence_of :since

  def do_this
    #a callback

# Using the `Relationship` model in `Node` models:
# app/models/student.rb
class Student
  include ActiveGraph::Node

  has_many :out, :lessons, rel_class: :EnrolledIn

# app/models/lesson.rb
class Lesson
  include ActiveGraph::Node

  has_many :in, :students, rel_class: :EnrolledIn

See also

There is also a screencast available reviewing Relationship:

Relationship Creation

From an Relationship Model

Once setup, Relationship models follow the same rules as Node in regard to properties. Declare them to create setter/getter methods. You can also set created_at or updated_at for automatic timestamps.

Relationship instances require related nodes before they can be saved. Set these using the from_node and to_node methods.

rel =
rel.from_node = student
rel.to_node = lesson

You can pass these as parameters when calling new or create if you so choose.

rel = student, to_node: lesson)
rel = EnrolledIn.create(from_node: student, to_node: lesson)

From a has_many or has_one association

Add the :rel_class option to an association with the name of an Relationship model. Association creation and querying will use this rel class, verifying classes, adding defaults, and performing callbacks.

class Student
  include ActiveGraph::Node
  has_many :out, :lessons, rel_class: :EnrolledIn

Creating Unique Relationships

The creates_unique class method will change the Cypher generated during rel creation from CREATE to CREATE UNIQUE. It may be called with one optional argument of the following:

  • :none, also used when no argument is given, will not include properties to determine whether ot not to create a unique relationship. This means that no more than one relationship of the same pairing of nodes, rel type, and direction will ever be created.
  • :all, which will include all set properties in rel creation. This means that if a new relationship will be created unless all nodes, type, direction, and rel properties are matched.
  • {on: [keys]} will use the keys given to determine whether to create a new rel and the remaining properties will be set afterwards.

Query and Loading existing relationships

Like nodes, you can load relationships a few different ways.

:each_rel, :each_with_rel, or :pluck methods

Any of these methods can return relationship objects.

Student.first.lessons.each_rel { |r| }
Student.first.lessons.each_with_rel { |node, rel| }

These are available as both class or instance methods. Because both each_rel and each_with_rel return enumerables when a block is skipped, you can take advantage of the full suite of enumerable methods:{ |n, r| r.grade > 85 }

Be aware that select would be performed in Ruby after a Cypher query is performed. The example above performs a Cypher query that matches all students with relationships of type enrolled_in to Lesson.first, then it would call select on that.

Advanced Usage

Separation of Relationship Logic

Relationship really shines when you have multiple associations that share a relationship type. You can use an Relationship model to separate the relationship logic and just let the node models be concerned with the labels of related objects.

class User
  include ActiveGraph::Node
  property :managed_stats, type: Integer #store the number of managed objects to improve performance

  has_many :out, :managed_lessons,  model_class: :Lesson,  rel_class: :ManagedRel
  has_many :out, :managed_teachers, model_class: :Teacher, rel_class: :ManagedRel
  has_many :out, :managed_events,   model_class: :Event,   rel_class: :ManagedRel
  has_many :out, :managed_objects,  model_class: false,    rel_class: :ManagedRel

  def update_stats
    managed_stats += 1

class ManagedRel
  include ActiveGraph::Relationship
  after_create :update_user_stats
  validate :manageable_object
  from_class :User
  to_class :any
  type 'MANAGES'

  def update_user_stats

  def manageable_object
    errors.add(:to_node) unless to_node.respond_to?(:managed_by)

# elsewhere
rel = user, to_node: any_node)
  # validation passed, to_node is a manageable object
  # something is wrong

Additional methods

:type instance method, _:type class method: return the relationship type of the model

:_from_class and :_to_class class methods: return the expected classes declared in the model

Regarding: from and to

:from_node, :to_node, :from_class, and :to_class all have aliases using start and end: :start_class, :end_class, :start_node, :end_node, :start_node=, :end_node=. This maintains consistency with elements of the ActiveGraph::Core API while offering what may be more natural options for Rails users.