Skip to content

Lightweight but flexible gem that allows reflect some of data from relational database into neo4j. This allows to perform faster and easier search of your models ids or it can be first step of migrating application data to neo4j.

License

Notifications You must be signed in to change notification settings

shhavel/neomirror

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Neomirror

Lightweight but flexible gem that allows reflect some of data from relational database into neo4j.
This allows to perform faster and easier search of your models ids
or it can be first step of migrating application data to neo4j.
Uses Neography (wrapper of Neo4j Rest API). Gem was inspired by Neoid

Installation

Add this line to your application's Gemfile:

gem 'neomirror'

And then execute:

$ bundle

Or install it yourself as:

$ gem install neomirror

Configuration

For more configuration options please read about Neography Configuration and initialization

Neography.configure do |config|
  config.protocol = "http://"
  config.server = "localhost"
  config.port = 7474
  config.username = nil
  config.password = nil
end

Neomirror.connection = Neography::Rest.new

Usage

Reflect model as node (vertex).

class User < ActiveRecord::Base
  include Neomirror::Node

  mirror_neo_node label: :User do # option :label is optional
    property :name
    property :name_length, ->(record) { record.name.length }
  end
end

user = User.create(name: 'Dougal')
# Find or create neo node.
user.neo_node # => #<Neography::Node id=...> 
user.node # Alias of #neo_node
user.find_neo_node # => #<Neography::Node id=...> 

Primary key is saved automatically for nodes as id attribute. Also unique constraint is created. Creating a unique constraint also creates a unique index (which is faster than a regular index).

For ActiveRecord methods #create_neo_node, #update_neo_node, #destroy_neo_node are called on corresponding callbacks (after_create, after_update, after_destroy).

Reflect model as one or several relationships (edges).

class Membership < ActiveRecord::Base
  include Neomirror::Relationship
  belongs_to :premises
  belongs_to :group

  mirror_neo_relationship start_node: :premises, end_node: :group, type: :MEMBER_OF
end

membership = Membership.first
# Find or create neo relationship.
membership.neo_relationship # => #<Neography::Relationship> or nil unless both nodes present
membership.neo_rel # Alias of #neo_relationship

For ActiveRecord methods #create_neo_relationships, #update_neo_relationships, #destroy_neo_relationships are called on corresponding callbacks (after_create, after_update, after_destroy).

Reflect and retrieve several relationships for model.

class Staff < ActiveRecord::Base
  include Neomirror::Relationship
  belongs_to :user
  belongs_to :premises
  belongs_to :group

  mirror_neo_relationship start_node: :user, end_node: :premises, type: :STAFF_OF
  mirror_neo_relationship start_node: :user, end_node: :group, type: :STAFF_OF
end

staff = Staff.first
staff.neo_relationship(end_node: :premises) # => #<Neography::Relationship> or nil
staff.neo_relationship(end_node: :group)    # => #<Neography::Relationship> or nil

Migration of existing data

Premises.find_each(&:create_neo_node)
Group.find_each(&:create_neo_node)
Membership.preload(:premises, :group).find_each(&:create_neo_relationships)
User.find_each(&:create_neo_node)
Staff.preload(:premises, :group).find_each(&:create_neo_relationships)

Note that #create_neo_node method will raise exception for already existed node and #create_neo_relationships will create duplicated relationships for existed relationships.

Also can use #neo_node and #neo_relationship methods which find or create.

Premises.find_each(&:neo_node)
Group.find_each(&:neo_node)
Membership.preload(:premises, :group).find_each(&:neo_relationship)
User.find_each(&:neo_node)
Staff.preload(:premises, :group).find_each do |staff|
  staff.neo_relationship(end_node: :premises)
  staff.neo_relationship(end_node: :group)
end

Clear neo4j

Neomirror.neo.execute_query("START n=node(*) OPTIONAL MATCH n-[r]-() DELETE n,r")

Reflect model as bunch of optional relationships

Sometimes there is choise how to reflect relationship. Model which is representation of relationship can be mapped as edge with properties or as bunch of edges. Both design decisions are possible with neomirror.

Reflect model as relationship (edge) with properties.

class Staff < ActiveRecord::Base
  include Neomirror::Relationship
  belongs_to :user
  belongs_to :premises

  mirror_neo_relationship start_node: :user, end_node: :premises, type: :STAFF_OF do
    property :roles
  end
end

Reflect model as bunch of optional relationships (edges) existence of which depends on the respective condition. On model create edge created only if predicate evaluates as true. On model update edge will be deleted if predicate evaluates as false.

class Staff < ActiveRecord::Base
  include Neomirror::Relationship
  belongs_to :user
  belongs_to :premises

  mirror_neo_relationship start_node: :user, end_node: :premises, type: :MANAGER_OF,
    if: ->(r) { r.roles.include?('manager') }

  mirror_neo_relationship start_node: :user, end_node: :premises, type: :VISITOR_OF,
    if: ->(r) { r.roles.include?('visitor') }
end

Even possible reflect model as node (vertex) and relationship(s) (edge).

class Group < ActiveRecord::Base
  include Neomirror::Node
  include Neomirror::Relationship

  belongs_to :parent, class_name: 'Group', foreign_key: :parent_id

  mirror_neo_node

  mirror_neo_relationship start_node: :self, end_node: :parent, type: :CHILD_OF
end

Compatibility

It is possible to use it outside of ActiveRecord (there is no dependency). Just use methods create_neo_node, update_neo_node and destroy_neo_node in your callbaks for nodes and create_neo_relationships, update_neo_relationships and destroy_neo_relationships for relationships.

Also specify primary key attribute if it is differ from id and class don't respond_to? :primary_key method.

class Postcode
  attr_accessor :code
  include Neomirror::Node

  self.neo_primary_key = :code

  mirror_neo_node
end

p = Postcode.new
p.code = 'ABC'
p.create_neo_node # => #<Neography::Node id="ABC">

Skip neomirror callbacks

Any callback (for both neo node and neo relationship) can be skipped if #skip_neo_callbacks is set to true.

# skip create_neo_node callback
user = User.new
user.skip_neo_callbacks = true
user.save
user.persisted? # => true
user.find_neo_node # => nil

# skip update_neo_node callback
user = User.last
user.skip_neo_callbacks = true
user.name # => Ted
user.neo_node.name # => Ted
user.update_attributes(name: 'Dougal')
user.name # => Dougal
user.neo_node.name # => Ted

Contributing

  1. Fork it ( http://github.com/shhavel/neomirror/fork )
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request

About

Lightweight but flexible gem that allows reflect some of data from relational database into neo4j. This allows to perform faster and easier search of your models ids or it can be first step of migrating application data to neo4j.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages