Welcome to Neo4j.rb’s documentation!¶
Contents:
Introduction¶
Neo4j.rb is an ActiveRecord-inspired OGM (Object Graph Mapping, like ORM) for Ruby supporting Neo4j 2.1+.
Terminology¶
Neo4j¶
- Node
- An Object or Entity which has a distinct identity. Can store arbitrary properties with values
- Label
- A means of identifying nodes. Nodes can have zero or more labels. While similar in concept to relational table names, nodes can have multiple labels (i.e. a node could have the labels
Person
andTeacher
) - Relationship
- A link from one node to another. Can store arbitrary properties with values. A direction is required but relationships can be traversed bi-directionally without a performance impact.
- Type
- Relationships always have exactly one type which describes how it is relating it’s source and destination nodes (i.e. a relationship with a
FRIEND_OF
type might connect twoPerson
nodes)
Neo4j.rb¶
Neo4j.rb consists of the neo4j and neo4j-core gems.
- neo4j
- Provides
ActiveNode
andActiveRel
modules for object modeling. Introduces Model and Association concepts (see below). Depends onneo4j-core
and thus both are available whenneo4j
is used - neo4j-core
- Provides low-level connectivity, transactions, and response object wrapping. Includes
Query
class for generating Cypher queries with Ruby method chaining. - Model
- A Ruby class including either the
Neo4j::ActiveNode
module (for modeling nodes) or theNeo4j::ActiveRel
module (for modeling relationships) from theneo4j
gem. These modules give classes the ability to define properties, associations, validations, and callbacks - Association
- Defined on an
ActiveNode
model. Defines either ahas_one
orhas_many
relationship to a model. A higher level abstraction of a Relationship
Code Examples¶
With Neo4j.rb, you can use either high-level abstractions for convenience or low level APIs for flexibility.
ActiveNode¶
ActiveNode provides an Object Graph Model (OGM) for abstracting Neo4j concepts with an ActiveRecord
-like API:
# Models to create nodes
person = Person.create(name: 'James', age: 15)
# Get object by attributes
person = Person.find_by(name: 'James', age: 15)
# Associations to traverse relationships
person.houses.map(&:address)
# Method-chaining to build and execute queries
Person.where(name: 'James').order(age: :desc).first
# Query building methods can be chained with associations
# Here we get other owners for pre-2005 vehicles owned by the person in question
person.vehicles(:v).where('v.year < 2005').owners(:other).to_a
Setup¶
The neo4j.rb gems (neo4j
and neo4j-core
) support both Ruby and JRuby and can be used with many different frameworks and services. If you’re just looking to get started you’ll probably want to use the neo4j
gem which includes neo4j-core
as a dependency.
Below are some instructions on how to get started:
Ruby on Rails¶
The following contains instructions on how to setup Neo4j with Rails. If you prefer a video to follow along you can use this YouTube video
There are two ways to add neo4j to your Rails project. You can LINK||generate a new project||LINK with Neo4j as the default model mapper or you can LINK||add it manually||LINK.
Generating a new app¶
To create a new Rails app with Neo4j as the default model mapper use -m
to run a script from the Neo4j projcet and -O
to exclude ActiveRecord like so:
rails new myapp -m http://neo4jrb.io/neo4j/neo4j.rb -O
Note
Due to network issues sometimes you may need to run this command two or three times for the file to download correctly
An example series of setup commands:
rails new myapp -m http://neo4jrb.io/neo4j/neo4j.rb -O
cd myapp
rake neo4j:install[community-latest]
rake neo4j:start
rails generate scaffold User name:string email:string
rails s
open http://localhost:3000/users
See also
Adding the gem to an existing project¶
Include in your Gemfile
:
# for rubygems
gem 'neo4j', '~> 5.0.0'
In application.rb
:
require 'neo4j/railtie'
Note
Neo4j does not interfere with ActiveRecord and both can be used in the same application
If you want the rails generate
command to generate Neo4j models by default you can modify application.rb
like so:
class Application < Rails::Application
# ...
config.generators { |g| g.orm :neo4j }
end
Rails configuration¶
For both new apps and existing apps the following configuration applies:
An example config/application.rb
file:
config.neo4j.session_type = :server_db
config.neo4j.session_path = 'http://localhost:7474'
Neo4j requires authentication by default but if you install using the built-in rake tasks) authentication is disabled. If you are using authentication you can configure it like this:
config.neo4j.session_options = { basic_auth: { username: 'foo', password: 'bar'} }
Any Ruby Project¶
Include either neo4j
or neo4j-core
in your Gemfile
(neo4j
includes neo4j-core
as a dependency):
gem 'neo4j', '~> 5.0.0'
# OR
gem 'neo4j-core', '~> 5.0.0'
If using only neo4j-core
you can optionally include the rake tasks (documentation) manually in your Rakefile
:
# Both are optional
# This provides tasks to install/start/stop/configure Neo4j
load 'neo4j/tasks/neo4j_server.rake'
# This provides tasks to have migrations
load 'neo4j/tasks/migration.rake'
If you don’t already have a server you can install one with the rake tasks from neo4j_server.rake
. See the (rake tasks documentation) for details on how to install, configure, and start/stop a Neo4j server in your project directory.
Connection¶
To open a session to the neo4j server database:
In Ruby¶
# In JRuby or MRI, using Neo4j Server mode. When the railtie is included, this happens automatically.
Neo4j::Session.open(:server_db)
Embedded mode in JRuby¶
In jRuby you can access the data in server mode as above. If you want to run the database in “embedded” mode, however you can configure it like this:
session = Neo4j::Session.open(:embedded_db, '/folder/db')
session.start
Embedded mode means that Neo4j is running inside your jRuby process. This allows for direct access to the Neo4j Java APIs for faster and more direct querying.
Heroku¶
Add a Neo4j db to your application:
# To use GraphineDB:
heroku addons:create graphenedb
# To use Graph Story:
heroku addons:create graphstory
See also
- GraphineDB
- https://devcenter.heroku.com/articles/graphenedb For plans: https://addons.heroku.com/graphenedb
- Graph Story
- https://devcenter.heroku.com/articles/graphstory For plans: https://addons.heroku.com/graphstory
Rails configuration¶
config/application.rb
config.neo4j.session_type = :server_db
# GraphineDB
config.neo4j.session_path = ENV["GRAPHENEDB_URL"] || 'http://localhost:7474'
# Graph Story
config.neo4j.session_path = ENV["GRAPHSTORY_URL"] || 'http://localhost:7474'
Rake Tasks¶
The neo4j-core
gem (automatically included with the neo4j
gem) includes some rake tasks which make it easy to install and manage a Neo4j server in the same directory as your Ruby project.
Note
If you are using zsh, you need to prefix any rake tasks with arguments with the noglob command, e.g. $ noglob bundle exec rake neo4j:install[community-latest]
.
- neo4j:install
Arguments:
version
andenvironment
(environment default is development)Example:
rake neo4j:install[community-latest,development]
Downloads and installs Neo4j into
$PROJECT_DIR/db/neo4j/<environment>/
For the
version
argument you can specify eithercommunity-latest
/enterprise-latest
to get the most up-to-date stable version or you can specify a specific version with the formatcommunity-x.x.x
/enterprise-x.x.x
- neo4j:config
Arguments:
environment
andport
Example:
rake neo4j:config[development,7100]
Configure the port which Neo4j runs on. This affects the HTTP REST interface and the web console address. This also sets the HTTPS port to the specified port minus one (so if you specify 7100 then the HTTP port will be 7099)
- neo4j:start
Arguments:
environment
Example:
rake neo4j:start[development]
Start the Neo4j server
Assuming everything is ok, point your browser to http://localhost:7474 and the Neo4j web console should load up.
- neo4j:start_no_wait
Arguments:
environment
Example:
rake neo4j:start_no_wait[development]
Start the Neo4j server with the
start-no-wait
command- neo4j:stop
Arguments:
environment
Example:
rake neo4j:stop[development]
Stop the Neo4j server
- neo4j:restart
Arguments:
environment
Example:
rake neo4j:restart[development]
Restart the Neo4j server
ActiveNode¶
ActiveNode is the ActiveRecord replacement module for Rails. Its syntax should be familiar for ActiveRecord users but has some unique qualities.
To use ActiveNode, include Neo4j::ActiveNode in a class.
class Post
include Neo4j::ActiveNode
end
Properties¶
All properties for Neo4j::ActiveNode objects must be declared (unlike neo4j-core nodes). Properties are declared using the property method which is the same as attribute from the active_attr gem.
Example:
class Post
include Neo4j::ActiveNode
property :title, index: :exact
property :text, default: 'bla bla bla'
property :score, type: Integer, default: 0
validates :title, :presence => true
validates :score, numericality: { only_integer: true }
before_save do
self.score = score * 100
end
has_n :friends
end
Properties can be indexed using the index argument on the property method, see example above.
See also
Indexes¶
To declare a index on a property
class Person
include Neo4j::ActiveNode
property :name, index: :exact
end
Only exact index is currently possible.
Indexes can also be declared like this:
class Person
include Neo4j::ActiveNode
property :name
index :name
end
Constraints¶
You can declare that a property should have a unique value.
class Person
property :id_number, constraint: :unique # will raise an exception if id_number is not unique
end
Notice an unique validation is not enough to be 100% sure that a property is unique (because of concurrency issues, just like ActiveRecord). Constraints can also be declared just like indexes separately, see above.
Labels¶
The class name maps directly to the label. In the following case both the class name and label are Post
class Post
include Neo4j::ActiveNode
end
If you want to specify a different label for your class you can use mapped_label_name
:
class Post
include Neo4j::ActiveNode
self.mapped_label_name = 'BlogPost'
end
If you would like to use multiple labels you can use class inheritance. In the following case object created with the Article model would have both Post and Article labels. When querying Article both labels are required on the nodes as well.
class Post
include Neo4j::ActiveNode
end
class Article < Post
end
Serialization¶
Pass a property name as a symbol to the serialize method if you want to save a hash or an array with mixed object types* to the database.
class Student
include Neo4j::ActiveNode
property :links
serialize :links
end
s = Student.create(links: { neo4j: 'http://www.neo4j.org', neotech: 'http://www.neotechnology.com' })
s.links
# => {"neo4j"=>"http://www.neo4j.org", "neotech"=>"http://www.neotechnology.com"}
s.links.class
# => Hash
Neo4j.rb serializes as JSON by default but pass it the constant Hash as a second parameter to serialize as YAML. Those coming from ActiveRecord will recognize this behavior, though Rails serializes as YAML by default.
Neo4j allows you to save Ruby arrays to undefined or String types but their contents need to all be of the same type. You can do user.stuff = [1, 2, 3] or user.stuff = [“beer, “pizza”, “doritos”] but not user.stuff = [1, “beer”, “pizza”]. If you wanted to do that, you could call serialize on your property in the model.
Wrapping¶
When loading a node from the database there is a process to determine which ActiveNode
model to choose for wrapping the node. If nothing is configured on your part then when a node is created labels will be saved representing all of the classes in the hierarchy.
That is, if you have a Teacher
class inheriting from a Person
model, then creating a Person
object will create a node in the database with a Person
label, but creating a Teacher
object will create a node with both the Teacher
and Person
labels.
If there is a value for the property defined by class_name_property then the value of that property will be used directly to determine the class to wrap the node in.
Callbacks¶
Implements like Active Records the following callback hooks:
- initialize
- validation
- find
- save
- create
- update
- destroy
created_at, updated_at¶
class Blog
include Neo4j::ActiveNode
property :updated_at # will automatically be set when model changes
end
Validation¶
Support the Active Model validation, such as:
validates :age, presence: true validates_uniqueness_of :name, :scope => :adult
id property (primary key)¶
Unique IDs are automatically created for all nodes using SecureRandom::uuid. See Unique IDs for details.
Associations¶
What follows is an overview of adding associations to models. For more detailed information, see Declared Relationships.
has_many and has_one associations can also be defined on ActiveNode models to make querying and creating relationships easier.
class Post
include Neo4j::ActiveNode
has_many :in, :comments, origin: :post
has_one :out, :author, type: :author, model_class: :Person
end
class Comment
include Neo4j::ActiveNode
has_one :out, :post, type: :post
has_one :out, :author, type: :author, model_class: :Person
end
class Person
include Neo4j::ActiveNode
has_many :in, :posts, origin: :author
has_many :in, :comments, origin: :author
end
You can query associations:
post.comments.to_a # Array of comments
comment.post # Post object
comment.post.comments # Original comment and all of it's siblings. Makes just one query
post.comments.authors.posts # All posts of people who have commented on the post. Still makes just one query
You can create associations
post.comments = [comment1, comment2] # Removes all existing relationships
post.comments << comment3 # Creates new relationship
comment.post = post1 # Removes all existing relationships
See also
Eager Loading¶
ActiveNode supports eager loading of associations in two ways. The first way is transparent. When you do the following:
person.blog_posts.each do |post|
puts post.title
puts "Tags: #{post.tags.map(&:name).join(', ')}"
post.comments.each do |comment|
puts ' ' + comment.title
end
end
Only three Cypher queries will be made:
- One to get the blog posts for the user
- One to get the tags for all of the blog posts
- One to get the comments for all of the blog posts
While three queries isn’t ideal, it is better than the naive approach of one query for every call to an object’s association (Thanks to DataMapper for the inspiration).
For those times when you need to load all of your data with one Cypher query, however, you can do the following to give ActiveNode a hint:
person.blog_posts.with_associations(:tags, :comments).each do |post|
puts post.title
puts "Tags: #{post.tags.map(&:name).join(', ')}"
post.comments.each do |comment|
puts ' ' + comment.title
end
end
All that we did here was add .with_associations(:tags, :comments)
. In addition to getting all of the blog posts, this will generate a Cypher query which uses the Cypher COLLECT() function to efficiently roll-up all of the associated objects. ActiveNode then automatically structures them into a nested set of ActiveNode objects for you.
ActiveRel¶
Note
See https://github.com/neo4jrb/neo4j/wiki/Neo4j.rb-v4-Introduction if you are using the master branch from this repo. It contains information about changes to the API.
ActiveRel is Neo4j.rb 3.0’s the relationship wrapper. ActiveRel objects share most of their behavior with ActiveNode objects. It is purely optional and offers advanced functionality for complex relationships.
When to Use?¶
It is not always necessary to use ActiveRel 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 ActiveRel doesn’t implement a uuid property like ActiveNode
.
Separation of relationship logic instead of shoehorning it into Node models
Validations, callbacks, custom methods, etc.
Centralize relationship type, no longer need to use :type
or :origin
options in models
Setup¶
ActiveRel model definitions have four requirements:
- include Neo4j::ActiveRel
- call from_class with a valid model constant or :any
- call to_class with a valid model constant or :any
- call type with a Symbol or String to define the Neo4j relationship type
See the note on from/to at the end of this page for additional information.
# app/models/enrolled_in.rb
class EnrolledIn
include Neo4j::ActiveRel
before_save :do_this
from_class Student
to_class Lesson
type 'enrolled_in'
property :since, type: Integer
property :grade, type: Integer
property :notes
validates_presence_of :since
def do_this
#a callback
end
end
Relationship Creation¶
From an ActiveRel Model¶
Once setup, ActiveRel models follow the same rules as ActiveNode in regard to properties. Declare them to create setter/getter methods. You can also set created_at
or updated_at
for automatic timestamps.
ActiveRel instances require related nodes before they can be saved. Set these using the from_node and to_node methods.
rel = EnrolledIn.new
rel.from_node = student
rel.to_node = lesson
You can pass these as parameters when calling new or create if you so choose.
rel = EnrolledIn.new(from_node: student, to_node: lesson)
#or
rel = EnrolledIn.create(from_node: student, to_node: lesson)
From a has_many or has_one association¶
Pass the :rel_class option in a declared association with the constant of an ActiveRel model. When that relationship is created, it will add a hidden _classname property with that model’s name. The association will use the type declared in the ActiveRel model and it will raise an error if it is included in more than one place.
class Student
include Neo4j::ActiveNode
has_many :out, :lessons, rel_class: :EnrolledIn
end
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| }
Student.first.query_as(:s).match('s-[rel1:`enrolled_in`]->n2').pluck(:rel1)
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:
Lesson.first.students.each_with_rel.select{ |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.
The :where method¶
Because you cannot search for a relationship the way you search for a node, ActiveRel’s where method searches for the relationship relative to the labels found in the from_class and to_class models. Therefore:
EnrolledIn.where(since: 2002)
# Generates the Cypher:
# "MATCH (node1:`Student`)-[rel1:`enrolled_in`]->(node2:`Lesson`) WHERE rel1.since = 2002 RETURN rel1"
If your from_class is :any, the same query looks like this:
"MATCH (node1)-[rel1:`enrolled_in`]->(node2:`Lesson`) WHERE rel1.since = 2002 RETURN rel1"
And if to_class is also :any, you end up with:
"MATCH (node1)-[rel1:`enrolled_in`]->(node2) WHERE rel1.since = 2002 RETURN rel1"
As a result, this combined with the inability to index relationship properties can result in extremely inefficient queries.
Advanced Usage¶
Separation of Relationship Logic¶
ActiveRel really shines when you have multiple associations that share a relationship type. You can use a rel model to separate the relationship logic and just let the node models be concerned with the labels of related objects.
class User
include Neo4j::ActiveNode
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
save
end
end
class ManagedRel
include Neo4j::ActiveRel
after_create :update_user_stats
validate :manageable_object
from_class User
to_class :any
type 'manages'
def update_user_stats
from_node.update_stats
end
def manageable_object
errors.add(:to_node) unless to_node.respond_to?(:managed_by)
end
end
# elsewhere
rel = ManagedRel.new(from_node: user, to_node: any_node)
if rel.save
# validation passed, to_node is a manageable object
else
# something is wrong
end
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 Neo4j::Core API while offering what may be more natural options for Rails users.
Querying¶
Simple Query Methods¶
There are a number of ways to find and return nodes.
.find
¶
Find an object by id_property (TODO: LINK TO id_property documentation)
.find_by
¶
find_by
and find_by!
behave as they do in ActiveRecord, returning the first object matching the criteria or nil (or an error in the case of find_by!
)
Post.find_by(title: 'Neo4j.rb is awesome')
Scope Method Chaining¶
Like in ActiveRecord you can build queries via method chaining. This can start in one of three ways:
Model.all
Model.association
model_object.association
In the case of the association calls, the scope becomes a class-level representation of the association’s model so far. So for example if I were to call post.comments
I would end up with a representation of nodes from the Comment
model, but only those which are related to the post
object via the comments
association.
At this point it should be mentioned that what associations return isn’t an Array
but in fact an AssociationProxy
. AssociationProxy
is Enumerable
so you can still iterate over it as a collection. This allows for the method chaining to build queries, but it also enables eager loading of associations
From a scope you can filter, sort, and limit to modify the query that will be performed or call a further association.
Querying the scope¶
Similar to ActiveRecord you can perform various operations on a scope like so:
lesson.teachers.where(name: /.* smith/i, age: 34).order(:name).limit(2)
The arguments to these methods are translated into Cypher
query statements. For example in the above statement the regular expression is translated into a Cypher =~
operator. Additionally all values are translated into Neo4j query parameters for the best performance and to avoid query injection attacks.
Chaining associations¶
As you’ve seen, it’s possible to chain methods to build a query on one model. In addition it’s possible to also call associations at any point along the chain to transition to another associated model. The simplest example would be:
student.lessons.teachers
This would returns all of the teachers for all of the lessons which the students is taking. Keep in mind that this builds only one Cypher query to be executed when the result is enumerated. Finally you can combine scoping and association chaining to create complex cypher query with simple Ruby method calls.
student.lessons(:l).where(level: 102).teachers(:t).where('t.age > 34').pluck(:l)
Here we get all of the lessons at the 102 level which have a teacher older than 34. The pluck
method will actually perform the query and return an Array
result with the lessons in question. There is also a return
method which returns an Array
of result objects which, in this case, would respond to a call to the #l
method to return the lesson.
Note here that we’re giving an argument to the associaton methods (lessons(:l)
and teachers(:t)
) in order to define Cypher variables which we can refer to. In the same way we can also pass in a second argument to define a variable for the relationship which the association follows:
student.lessons(:l, :r).where("r.start_date < {the_date} and r.end_date >= {the_date}").params(the_date: '2014-11-22').pluck(:l)
Here we are limiting lessons by the start_date
and end_date
on the relationship between the student and the lessons. We can also use the rel_where
method to filter based on this relationship:
student.lessons.where(subject: 'Math').rel_where(grade: 85)
Associations and Unpersisted Nodes¶
There is some special behavior around association creation when nodes are new and unsaved. Below are a few scenarios and their outcomes.
When both nodes are persisted, associations changes using <<
or =
take place immediately – no need to call save.
student = Student.first
Lesson = Lesson.first
student.lessons << lesson
In that case, the relationship would be created immediately.
When the node on which the association is called is unpersisted, no changes are made to the database until save
is called. Once that happens, a cascading save event will occur.
student = Student.new
lesson = Lesson.first || Lesson.new
# This method will not save `student` or change relationships in the database:
student.lessons << lesson
Once we call save
on student
, two or three things will happen:
- Since
student
is unpersisted, it will be saved - If
lesson
is unpersisted, it will be saved - Once both nodes are saved, the relationship will be created
This process occurs within a transaction. If any part fails, an error will be raised, the transaction will fail, and no changes will be made to the database.
Finally, if you try to associate an unpersisted node with a persisted node, the unpersisted node will be saved and the relationship will be created immediately:
student = Student.first
lesson = Lesson.new
student.lessons << lesson
In the above example, lesson
would be saved and the relationship would be created immediately. There is no need to call save
on student
.
Parameters¶
If you need to use a string in where, you should set the parameter manually.
Student.all.where("s.age < {age} AND s.name = {name} AND s.home_town = {home_town}")
.params(age: params[:age], name: params[:name], home_town: params[:home_town])
.pluck(:s)
Variable-length relationships¶
It is possible to specify a variable-length qualifier to apply to relationships when calling association methods.
student.friends(rel_length: 2)
This would find the friends of friends of a student. Note that you can still name matched nodes and relationships and use those names to build your query as seen above:
student.friends(:f, :r, rel_length: 2).where('f.gender = {gender} AND r.since >= {date}').params(gender: 'M', date: 1.month.ago)
Note
You can either pass a single options Hash or provide both the node and relationship names along with the optional Hash.
There are many ways to provide the length information to generate all the various possibilities Cypher offers:
# As a Fixnum:
## Cypher: -[:`FRIENDS`*2]->
student.friends(rel_length: 2)
# As a Range:
## Cypher: -[:`FRIENDS`*1..3]->
student.friends(rel_length: 1..3) # Get up to 3rd degree friends
# As a Hash:
## Cypher: -[:`FRIENDS`*1..3]->
student.friends(rel_length: {min: 1, max: 3})
## Cypher: -[:`FRIENDS`*0..]->
student.friends(rel_length: {min: 0})
## Cypher: -[:`FRIENDS`*..3]->
student.friends(rel_length: {max: 3})
# As the :any Symbol:
## Cypher: -[:`FRIENDS`*]->
student.friends(rel_length: :any)
Caution
By default, “*..3” is equivalent to “*1..3” and “*” is equivalent to “*1..”, but this may change depending on your Node4j server configuration. Keep that in mind when using variable-length relationships queries without specifying a minimum value.
Note
When using variable-length relationships queries on has_one associations, be aware that multiple nodes could be returned!
The Query API¶
The neo4j-core
gem provides a Query
class which can be used for building very specific queries with method chaining. This can be used either by getting a fresh Query
object from a Session
or by building a Query
off of a scope such as above.
Neo4j::Session.current.query # Get a new Query object
# Get a Query object based on a scope
Student.query_as(:s)
student.lessons.query_as(:l)
The Query
class has a set of methods which map directly to Cypher clauses and which return another Query
object to allow chaining. For example:
- student.lessons.query_as(:l) # This gives us our first Query object
- .match(“l-[:has_category*]->(root_category:Category)”).where(“NOT(root_category-[:has_category]->())) .pluck(:root_category)
Here we can make our own MATCH
clauses unlike in model scoping. We have where
, pluck
, and return
here as well in addition to all of the other clause-methods. See this page for more details.
TODO Duplicate this page and link to it from here (or just duplicate it here): https://github.com/neo4jrb/neo4j-core/wiki/Queries
match_to
and first_rel_to
¶
There are two methods, match_to and first_rel_to that both make simple patterns easier.
In the most recent release, match_to accepts nodes; in the master branch and in future releases, it will accept a node or an ID. It is essentially shorthand for association.where(neo_id: node.neo_id) and returns a QueryProxy object.
# starting from a student, match them to a lesson based off of submitted params, then return students in their classes
student.lessons.match_to(params[:id]).students
first_rel_to will return the first relationship found between two nodes in a QueryProxy chain.
student.lessons.first_rel_to(lesson)
# or in the master branch, future releases
student.lessons.first_rel_to(lesson.id)
This returns a relationship object.
Finding in Batches¶
Finding in batches will soon be supported in the neo4j gem, but for now is provided in the neo4j-core gem (documentation)
Orm_Adapter¶
You can also use the orm_adapter API, by calling #to_adapter on your class. See the API, https://github.com/ianwhite/orm_adapter
Find or Create By...¶
QueryProxy has a find_or_create_by
method to make the node rel creation process easier. Its usage is simple:
a_node.an_association(params_hash)
The method has branching logic that attempts to match an existing node and relationship. If the pattern is not found, it tries to find a node of the expected class and create the relationship. If that doesn’t work, it creates the node, then creates the relationship. The process is wrapped in a transaction to prevent a failure from leaving the database in an inconsistent state.
There are some mild caveats. First, it will not work on associations of class methods. Second, you should not use it across more than one associations or you will receive an error. For instance, if you did this:
student.friends.lessons.find_or_create_by(subject: 'Math')
Assuming the lessons
association points to a Lesson
model, you would effectively end up with this:
math = Lesson.find_or_create_by(subject: 'Math')
student.friends.lessons << math
...which is invalid and will result in an error.
QueryClauseMethods¶
The Neo4j::Core::Query
class from the neo4j-core gem defines a DSL which allows for easy creation of Neo4j Cypher queries. They can be started from a session like so:
# The current session can be retrieved with `Neo4j::Session.current`
a_session.query
Advantages of using the Query class include:
- Method chaining allows you to build a part of a query and then pass it somewhere else to be built further
- Automatic use of parameters when possible
- Ability to pass in data directly from other sources (like Hash to match keys/values)
- Ability to use native Ruby objects (such as translating nil values to IS NULL, regular expressions to Cypher-style regular expression matches, etc...)
Below is a series of Ruby code samples and the resulting Cypher that would be generated. These examples are all generated directly from the spec file and are thus all tested to work.
Neo4j::Core::Query¶
#match¶
Ruby: | .match('n')
|
---|---|
Cypher: | MATCH n
|
Ruby: | .match(:n)
|
---|---|
Cypher: | MATCH n
|
Ruby: | .match(n: Person)
|
---|---|
Cypher: | MATCH (n:`Person`)
|
Ruby: | .match(n: 'Person')
|
---|---|
Cypher: | MATCH (n:`Person`)
|
Ruby: | .match(n: ':Person')
|
---|---|
Cypher: | MATCH (n:Person)
|
Ruby: | .match(n: :Person)
|
---|---|
Cypher: | MATCH (n:`Person`)
|
Ruby: | .match(n: [:Person, "Animal"])
|
---|---|
Cypher: | MATCH (n:`Person`:`Animal`)
|
Ruby: | .match(n: ' :Person')
|
---|---|
Cypher: | MATCH (n:Person)
|
Ruby: | .match(n: nil)
|
---|---|
Cypher: | MATCH (n)
|
Ruby: | .match(n: 'Person {name: "Brian"}')
|
---|---|
Cypher: | MATCH (n:Person {name: "Brian"})
|
Ruby: | .match(n: {name: 'Brian', age: 33})
|
---|---|
Cypher: | MATCH (n {name: {n_name}, age: {n_age}})
|
Parameters: {:n_name=>"Brian", :n_age=>33}
Ruby: | .match(n: {Person: {name: 'Brian', age: 33}})
|
---|---|
Cypher: | MATCH (n:`Person` {name: {n_Person_name}, age: {n_Person_age}})
|
Parameters: {:n_Person_name=>"Brian", :n_Person_age=>33}
Ruby: | .match('n--o')
|
---|---|
Cypher: | MATCH n--o
|
Ruby: | .match('n--o').match('o--p')
|
---|---|
Cypher: | MATCH n--o, o--p
|
#optional_match¶
Ruby: | .optional_match(n: Person)
|
---|---|
Cypher: | OPTIONAL MATCH (n:`Person`)
|
Ruby: | .match('m--n').optional_match('n--o').match('o--p')
|
---|---|
Cypher: | MATCH m--n, o--p OPTIONAL MATCH n--o
|
#using¶
Ruby: | .using('INDEX m:German(surname)')
|
---|---|
Cypher: | USING INDEX m:German(surname)
|
Ruby: | .using('SCAN m:German')
|
---|---|
Cypher: | USING SCAN m:German
|
Ruby: | .using('INDEX m:German(surname)').using('SCAN m:German')
|
---|---|
Cypher: | USING INDEX m:German(surname) USING SCAN m:German
|
#where¶
Ruby: | .where()
|
---|---|
Cypher: |
|
Ruby: | .where({})
|
---|---|
Cypher: |
|
Ruby: | .where('q.age > 30')
|
---|---|
Cypher: | WHERE (q.age > 30)
|
Ruby: | .where('q.age' => 30)
|
---|---|
Cypher: | WHERE (q.age = {q_age})
|
Parameters: {:q_age=>30}
Ruby: | .where('q.age' => [30, 32, 34])
|
---|---|
Cypher: | WHERE (q.age IN {q_age})
|
Parameters: {:q_age=>[30, 32, 34]}
Ruby: | .where('q.age IN {age}', age: [30, 32, 34])
|
---|---|
Cypher: | WHERE (q.age IN {age})
|
Parameters: {:age=>[30, 32, 34]}
Ruby: | .where('q.name =~ ?', '.*test.*')
|
---|---|
Cypher: | WHERE (q.name =~ {question_mark_param1})
|
Parameters: {:question_mark_param1=>".*test.*"}
Ruby: | .where('q.age IN ?', [30, 32, 34])
|
---|---|
Cypher: | WHERE (q.age IN {question_mark_param1})
|
Parameters: {:question_mark_param1=>[30, 32, 34]}
Ruby: | .where('q.age IN ?', [30, 32, 34]).where('q.age != ?', 60)
|
---|---|
Cypher: | WHERE (q.age IN {question_mark_param1}) AND (q.age != {question_mark_param2})
|
Parameters: {:question_mark_param1=>[30, 32, 34], :question_mark_param2=>60}
Ruby: | .where(q: {age: [30, 32, 34]})
|
---|---|
Cypher: | WHERE (q.age IN {q_age})
|
Parameters: {:q_age=>[30, 32, 34]}
Ruby: | .where('q.age' => nil)
|
---|---|
Cypher: | WHERE (q.age IS NULL)
|
Ruby: | .where(q: {age: nil})
|
---|---|
Cypher: | WHERE (q.age IS NULL)
|
Ruby: | .where(q: {neo_id: 22})
|
---|---|
Cypher: | WHERE (ID(q) = {ID_q})
|
Parameters: {:ID_q=>22}
Ruby: | .where(q: {age: 30, name: 'Brian'})
|
---|---|
Cypher: | WHERE (q.age = {q_age} AND q.name = {q_name})
|
Parameters: {:q_age=>30, :q_name=>"Brian"}
Ruby: | .where(q: {age: 30, name: 'Brian'}).where('r.grade = 80')
|
---|---|
Cypher: | WHERE (q.age = {q_age} AND q.name = {q_name}) AND (r.grade = 80)
|
Parameters: {:q_age=>30, :q_name=>"Brian"}
Ruby: | .where(q: {age: (30..40)})
|
---|---|
Cypher: | WHERE (q.age IN RANGE({q_age_range_min}, {q_age_range_max}))
|
Parameters: {:q_age_range_min=>30, :q_age_range_max=>40}
#match_nodes¶
one node object¶
Ruby: | .match_nodes(var: node_object)
|
---|---|
Cypher: | MATCH var WHERE (ID(var) = {ID_var})
|
Parameters: {:ID_var=>246}
integer¶
Ruby: | .match_nodes(var: 924)
|
---|---|
Cypher: | MATCH var WHERE (ID(var) = {ID_var})
|
Parameters: {:ID_var=>924}
two node objects¶
Ruby: | .match_nodes(user: user, post: post)
|
---|---|
Cypher: | MATCH user, post WHERE (ID(user) = {ID_user}) AND (ID(post) = {ID_post})
|
Parameters: {:ID_user=>246, :ID_post=>123}
node object and integer¶
Ruby: | .match_nodes(user: user, post: 652)
|
---|---|
Cypher: | MATCH user, post WHERE (ID(user) = {ID_user}) AND (ID(post) = {ID_post})
|
Parameters: {:ID_user=>246, :ID_post=>652}
#unwind¶
Ruby: | .unwind('val AS x')
|
---|---|
Cypher: | UNWIND val AS x
|
Ruby: | .unwind(x: :val)
|
---|---|
Cypher: | UNWIND val AS x
|
Ruby: | .unwind(x: 'val')
|
---|---|
Cypher: | UNWIND val AS x
|
Ruby: | .unwind(x: [1,3,5])
|
---|---|
Cypher: | UNWIND [1, 3, 5] AS x
|
Ruby: | .unwind(x: [1,3,5]).unwind('val as y')
|
---|---|
Cypher: | UNWIND [1, 3, 5] AS x UNWIND val as y
|
#return¶
Ruby: | .return('q')
|
---|---|
Cypher: | RETURN q
|
Ruby: | .return(:q)
|
---|---|
Cypher: | RETURN q
|
Ruby: | .return('q.name, q.age')
|
---|---|
Cypher: | RETURN q.name, q.age
|
Ruby: | .return(q: [:name, :age], r: :grade)
|
---|---|
Cypher: | RETURN q.name, q.age, r.grade
|
Ruby: | .return(q: :neo_id)
|
---|---|
Cypher: | RETURN ID(q)
|
Ruby: | .return(q: [:neo_id, :prop])
|
---|---|
Cypher: | RETURN ID(q), q.prop
|
#order¶
Ruby: | .order('q.name')
|
---|---|
Cypher: | ORDER BY q.name
|
Ruby: | .order_by('q.name')
|
---|---|
Cypher: | ORDER BY q.name
|
Ruby: | .order('q.age', 'q.name DESC')
|
---|---|
Cypher: | ORDER BY q.age, q.name DESC
|
Ruby: | .order(q: :age)
|
---|---|
Cypher: | ORDER BY q.age
|
Ruby: | .order(q: [:age, {name: :desc}])
|
---|---|
Cypher: | ORDER BY q.age, q.name DESC
|
Ruby: | .order(q: [:age, {name: :desc, grade: :asc}])
|
---|---|
Cypher: | ORDER BY q.age, q.name DESC, q.grade ASC
|
Ruby: | .order(q: {age: :asc, name: :desc})
|
---|---|
Cypher: | ORDER BY q.age ASC, q.name DESC
|
Ruby: | .order(q: [:age, 'name desc'])
|
---|---|
Cypher: | ORDER BY q.age, q.name desc
|
#limit¶
Ruby: | .limit(3)
|
---|---|
Cypher: | LIMIT {limit_3}
|
Parameters: {:limit_3=>3}
Ruby: | .limit('3')
|
---|---|
Cypher: | LIMIT {limit_3}
|
Parameters: {:limit_3=>3}
Ruby: | .limit(3).limit(5)
|
---|---|
Cypher: | LIMIT {limit_5}
|
Parameters: {:limit_5=>5}
#skip¶
Ruby: | .skip(5)
|
---|---|
Cypher: | SKIP {skip_5}
|
Parameters: {:skip_5=>5}
Ruby: | .skip('5')
|
---|---|
Cypher: | SKIP {skip_5}
|
Parameters: {:skip_5=>5}
Ruby: | .skip(5).skip(10)
|
---|---|
Cypher: | SKIP {skip_10}
|
Parameters: {:skip_10=>10}
Ruby: | .offset(6)
|
---|---|
Cypher: | SKIP {skip_6}
|
Parameters: {:skip_6=>6}
#with¶
Ruby: | .with('n.age AS age')
|
---|---|
Cypher: | WITH n.age AS age
|
Ruby: | .with('n.age AS age', 'count(n) as c')
|
---|---|
Cypher: | WITH n.age AS age, count(n) as c
|
Ruby: | .with(['n.age AS age', 'count(n) as c'])
|
---|---|
Cypher: | WITH n.age AS age, count(n) as c
|
Ruby: | .with(age: 'n.age')
|
---|---|
Cypher: | WITH n.age AS age
|
#create¶
Ruby: | .create('(:Person)')
|
---|---|
Cypher: | CREATE (:Person)
|
Ruby: | .create(:Person)
|
---|---|
Cypher: | CREATE (:Person)
|
Ruby: | .create(age: 41, height: 70)
|
---|---|
Cypher: | CREATE ( {age: {age}, height: {height}})
|
Parameters: {:age=>41, :height=>70}
Ruby: | .create(Person: {age: 41, height: 70})
|
---|---|
Cypher: | CREATE (:`Person` {age: {Person_age}, height: {Person_height}})
|
Parameters: {:Person_age=>41, :Person_height=>70}
Ruby: | .create(q: {Person: {age: 41, height: 70}})
|
---|---|
Cypher: | CREATE (q:`Person` {age: {q_Person_age}, height: {q_Person_height}})
|
Parameters: {:q_Person_age=>41, :q_Person_height=>70}
Ruby: | .create(q: {Person: {age: nil, height: 70}})
|
---|---|
Cypher: | CREATE (q:`Person` {age: {q_Person_age}, height: {q_Person_height}})
|
Parameters: {:q_Person_age=>nil, :q_Person_height=>70}
#create_unique¶
Ruby: | .create_unique('(:Person)')
|
---|---|
Cypher: | CREATE UNIQUE (:Person)
|
Ruby: | .create_unique(:Person)
|
---|---|
Cypher: | CREATE UNIQUE (:Person)
|
Ruby: | .create_unique(age: 41, height: 70)
|
---|---|
Cypher: | CREATE UNIQUE ( {age: {age}, height: {height}})
|
Parameters: {:age=>41, :height=>70}
Ruby: | .create_unique(Person: {age: 41, height: 70})
|
---|---|
Cypher: | CREATE UNIQUE (:`Person` {age: {Person_age}, height: {Person_height}})
|
Parameters: {:Person_age=>41, :Person_height=>70}
Ruby: | .create_unique(q: {Person: {age: 41, height: 70}})
|
---|---|
Cypher: | CREATE UNIQUE (q:`Person` {age: {q_Person_age}, height: {q_Person_height}})
|
Parameters: {:q_Person_age=>41, :q_Person_height=>70}
#merge¶
Ruby: | .merge('(:Person)')
|
---|---|
Cypher: | MERGE (:Person)
|
Ruby: | .merge(:Person)
|
---|---|
Cypher: | MERGE (:Person)
|
Ruby: | .merge(age: 41, height: 70)
|
---|---|
Cypher: | MERGE ( {age: {age}, height: {height}})
|
Parameters: {:age=>41, :height=>70}
Ruby: | .merge(Person: {age: 41, height: 70})
|
---|---|
Cypher: | MERGE (:`Person` {age: {Person_age}, height: {Person_height}})
|
Parameters: {:Person_age=>41, :Person_height=>70}
Ruby: | .merge(q: {Person: {age: 41, height: 70}})
|
---|---|
Cypher: | MERGE (q:`Person` {age: {q_Person_age}, height: {q_Person_height}})
|
Parameters: {:q_Person_age=>41, :q_Person_height=>70}
#delete¶
Ruby: | .delete('n')
|
---|---|
Cypher: | DELETE n
|
Ruby: | .delete(:n)
|
---|---|
Cypher: | DELETE n
|
Ruby: | .delete('n', :o)
|
---|---|
Cypher: | DELETE n, o
|
Ruby: | .delete(['n', :o])
|
---|---|
Cypher: | DELETE n, o
|
#set_props¶
Ruby: | .set_props('n = {name: "Brian"}')
|
---|---|
Cypher: | SET n = {name: "Brian"}
|
Ruby: | .set_props(n: {name: 'Brian', age: 30})
|
---|---|
Cypher: | SET n = {n_set_props}
|
Parameters: {:n_set_props=>{:name=>"Brian", :age=>30}}
#set¶
Ruby: | .set('n = {name: "Brian"}')
|
---|---|
Cypher: | SET n = {name: "Brian"}
|
Ruby: | .set(n: {name: 'Brian', age: 30})
|
---|---|
Cypher: | SET n.`name` = {setter_n_name}, n.`age` = {setter_n_age}
|
Parameters: {:setter_n_name=>"Brian", :setter_n_age=>30}
Ruby: | .set(n: {name: 'Brian', age: 30}, o: {age: 29})
|
---|---|
Cypher: | SET n.`name` = {setter_n_name}, n.`age` = {setter_n_age}, o.`age` = {setter_o_age}
|
Parameters: {:setter_n_name=>"Brian", :setter_n_age=>30, :setter_o_age=>29}
Ruby: | .set(n: {name: 'Brian', age: 30}).set_props('o.age = 29')
|
---|---|
Cypher: | SET n.`name` = {setter_n_name}, n.`age` = {setter_n_age}, o.age = 29
|
Parameters: {:setter_n_name=>"Brian", :setter_n_age=>30}
Ruby: | .set(n: :Label)
|
---|---|
Cypher: | SET n:`Label`
|
Ruby: | .set(n: [:Label, 'Foo'])
|
---|---|
Cypher: | SET n:`Label`, n:`Foo`
|
Ruby: | .set(n: nil)
|
---|---|
Cypher: |
|
#on_create_set¶
Ruby: | .on_create_set('n = {name: "Brian"}')
|
---|---|
Cypher: | ON CREATE SET n = {name: "Brian"}
|
Ruby: | .on_create_set(n: {})
|
---|---|
Cypher: |
|
Ruby: | .on_create_set(n: {name: 'Brian', age: 30})
|
---|---|
Cypher: | ON CREATE SET n.`name` = {setter_n_name}, n.`age` = {setter_n_age}
|
Parameters: {:setter_n_name=>"Brian", :setter_n_age=>30}
Ruby: | .on_create_set(n: {name: 'Brian', age: 30}, o: {age: 29})
|
---|---|
Cypher: | ON CREATE SET n.`name` = {setter_n_name}, n.`age` = {setter_n_age}, o.`age` = {setter_o_age}
|
Parameters: {:setter_n_name=>"Brian", :setter_n_age=>30, :setter_o_age=>29}
Ruby: | .on_create_set(n: {name: 'Brian', age: 30}).on_create_set('o.age = 29')
|
---|---|
Cypher: | ON CREATE SET n.`name` = {setter_n_name}, n.`age` = {setter_n_age}, o.age = 29
|
Parameters: {:setter_n_name=>"Brian", :setter_n_age=>30}
#on_match_set¶
Ruby: | .on_match_set('n = {name: "Brian"}')
|
---|---|
Cypher: | ON MATCH SET n = {name: "Brian"}
|
Ruby: | .on_match_set(n: {})
|
---|---|
Cypher: |
|
Ruby: | .on_match_set(n: {name: 'Brian', age: 30})
|
---|---|
Cypher: | ON MATCH SET n.`name` = {setter_n_name}, n.`age` = {setter_n_age}
|
Parameters: {:setter_n_name=>"Brian", :setter_n_age=>30}
Ruby: | .on_match_set(n: {name: 'Brian', age: 30}, o: {age: 29})
|
---|---|
Cypher: | ON MATCH SET n.`name` = {setter_n_name}, n.`age` = {setter_n_age}, o.`age` = {setter_o_age}
|
Parameters: {:setter_n_name=>"Brian", :setter_n_age=>30, :setter_o_age=>29}
Ruby: | .on_match_set(n: {name: 'Brian', age: 30}).on_match_set('o.age = 29')
|
---|---|
Cypher: | ON MATCH SET n.`name` = {setter_n_name}, n.`age` = {setter_n_age}, o.age = 29
|
Parameters: {:setter_n_name=>"Brian", :setter_n_age=>30}
#remove¶
Ruby: | .remove('n.prop')
|
---|---|
Cypher: | REMOVE n.prop
|
Ruby: | .remove('n:American')
|
---|---|
Cypher: | REMOVE n:American
|
Ruby: | .remove(n: 'prop')
|
---|---|
Cypher: | REMOVE n.prop
|
Ruby: | .remove(n: :American)
|
---|---|
Cypher: | REMOVE n:`American`
|
Ruby: | .remove(n: [:American, "prop"])
|
---|---|
Cypher: | REMOVE n:`American`, n.prop
|
Ruby: | .remove(n: :American, o: 'prop')
|
---|---|
Cypher: | REMOVE n:`American`, o.prop
|
Ruby: | .remove(n: ':prop')
|
---|---|
Cypher: | REMOVE n:`prop`
|
#start¶
Ruby: | .start('r=node:nodes(name = "Brian")')
|
---|---|
Cypher: | START r=node:nodes(name = "Brian")
|
Ruby: | .start(r: 'node:nodes(name = "Brian")')
|
---|---|
Cypher: | START r = node:nodes(name = "Brian")
|
clause combinations¶
Ruby: | .match(q: Person).where('q.age > 30')
|
---|---|
Cypher: | MATCH (q:`Person`) WHERE (q.age > 30)
|
Ruby: | .where('q.age > 30').match(q: Person)
|
---|---|
Cypher: | MATCH (q:`Person`) WHERE (q.age > 30)
|
Ruby: | .where('q.age > 30').start('n').match(q: Person)
|
---|---|
Cypher: | START n MATCH (q:`Person`) WHERE (q.age > 30)
|
Ruby: | .match(q: {age: 30}).set_props(q: {age: 31})
|
---|---|
Cypher: | MATCH (q {age: {q_age}}) SET q = {q_set_props}
|
Parameters: {:q_age=>30, :q_set_props=>{:age=>31}}
Ruby: | .match(q: Person).with('count(q) AS count')
|
---|---|
Cypher: | MATCH (q:`Person`) WITH count(q) AS count
|
Ruby: | .match(q: Person).with('count(q) AS count').where('count > 2')
|
---|---|
Cypher: | MATCH (q:`Person`) WITH count(q) AS count WHERE (count > 2)
|
Ruby: | .match(q: Person).with(count: 'count(q)').where('count > 2').with(new_count: 'count + 5')
|
---|---|
Cypher: | MATCH (q:`Person`) WITH count(q) AS count WHERE (count > 2) WITH count + 5 AS new_count
|
Ruby: | .match(q: Person).match('r:Car').break.match('(p: Person)-->q')
|
---|---|
Cypher: | MATCH (q:`Person`), r:Car MATCH (p: Person)-->q
|
Ruby: | .match(q: Person).break.match('r:Car').break.match('(p: Person)-->q')
|
---|---|
Cypher: | MATCH (q:`Person`) MATCH r:Car MATCH (p: Person)-->q
|
Ruby: | .match(q: Person).match('r:Car').break.break.match('(p: Person)-->q')
|
---|---|
Cypher: | MATCH (q:`Person`), r:Car MATCH (p: Person)-->q
|
Ruby: | .with(:a).order(a: {name: :desc}).where(a: {name: 'Foo'})
|
---|---|
Cypher: | WITH a ORDER BY a.name DESC WHERE (a.name = {a_name})
|
Parameters: {:a_name=>"Foo"}
Ruby: | .with(:a).limit(2).where(a: {name: 'Foo'})
|
---|---|
Cypher: | WITH a LIMIT {limit_2} WHERE (a.name = {a_name})
|
Parameters: {:a_name=>"Foo", :limit_2=>2}
Ruby: | .with(:a).order(a: {name: :desc}).limit(2).where(a: {name: 'Foo'})
|
---|---|
Cypher: | WITH a ORDER BY a.name DESC LIMIT {limit_2} WHERE (a.name = {a_name})
|
Parameters: {:a_name=>"Foo", :limit_2=>2}
Ruby: | .order(a: {name: :desc}).with(:a).where(a: {name: 'Foo'})
|
---|---|
Cypher: | WITH a ORDER BY a.name DESC WHERE (a.name = {a_name})
|
Parameters: {:a_name=>"Foo"}
Ruby: | .limit(2).with(:a).where(a: {name: 'Foo'})
|
---|---|
Cypher: | WITH a LIMIT {limit_2} WHERE (a.name = {a_name})
|
Parameters: {:a_name=>"Foo", :limit_2=>2}
Ruby: | .order(a: {name: :desc}).limit(2).with(:a).where(a: {name: 'Foo'})
|
---|---|
Cypher: | WITH a ORDER BY a.name DESC LIMIT {limit_2} WHERE (a.name = {a_name})
|
Parameters: {:a_name=>"Foo", :limit_2=>2}
Ruby: | .match(q: Person).where('q.age = {age}').params(age: 15)
|
---|---|
Cypher: | MATCH (q:`Person`) WHERE (q.age = {age})
|
Parameters: {:age=>15}
Configuration¶
- class_name_property
Default:
:_classname
Which property should be used to determine the ActiveNode class to wrap the node in
If there is no value for this property on a node the node`s labels will be used to determine the ActiveNode class
See also
- include_root_in_json
Default:
true
When serializing
ActiveNode
andActiveRel
objects, should there be a root in the JSON of the model name.- transform_rel_type
Default:
:upcase
Available values:
:upcase
,:downcase
,:legacy
,:none
Determines how relationship types as specified in associations are transformed when stored in the database. By default this is upper-case to match with Neo4j convention so if you specify an association of
has_many :in, :posts, type: :has_post
then the relationship type in the database will beHAS_POST
:legacy
- Causes the type to be downcased and preceded by a #
:none
- Uses the type as specified
- module_handling
Default:
:none
Available values:
:demodulize
,:none
,proc
Determines what, if anything, should be done to module names when a model’s class is set. By default, there is a direct mapping of model name to label, so MyModule::MyClass results in a label with the same name.
The :demodulize option uses ActiveSupport’s method of the same name to strip off modules. If you use a proc, it will the class name as an argument and you should return a string that modifies it as you see fit.
- association_model_namespace
Default:
nil
Associations defined in node models will try to match association names to classes. For example, has_many :out, :student will look for a Student class. To avoid having to use model_class: ‘MyModule::Student’, this config option lets you specify the module that should be used globally for class name discovery.
Of course, even with this option set, you can always override it by calling model_class: ‘ClassName’.
- logger
Default:
nil
(orRails.logger
in Rails)A Ruby
Logger
object which is used to log Cypher queries (info level is used)
API¶
Neo4j¶
Config¶
== Keeps configuration for neo4j
== Configurations keys
Constants¶
- DEFAULT_FILE
- CLASS_NAME_PROPERTY_KEY
Files¶
Methods¶
.[]
- .[]=
Sets the value of a config entry.
+ show/hide code
.association_model_namespace
.association_model_namespace_string
.class_name_property
- .configuration
Reads from the default_file if configuration is not set already
+ show/hide code
.default_file
- .default_file=
Sets the location of the configuration YAML file and old deletes configurations.
+ show/hide code
.defaults
- .delete
Remove the value of a config entry.
+ show/hide code
- .delete_all
Remove all configuration. This can be useful for testing purpose.
+ show/hide code
.include_root_in_json
.module_handling
.to_hash
.to_yaml
- .use
Yields the configuration
+ show/hide code
Railtie¶
Constants¶
Files¶
Methods¶
.java_platform?
.open_neo4j_session
#register_neo4j_cypher_logging
.setup_config_defaults!
.setup_default_session
.start_embedded_session
Migration¶
AddIdProperty¶
Constants¶
Methods¶
#default_path
#initialize
#joined_path
#migrate
- #models_filename
Returns the value of attribute models_filename
+ show/hide code
#output
#print_output
#setup
Constants¶
Paginated¶
Constants¶
Methods¶
.create_from
- #current_page
Returns the value of attribute current_page
+ show/hide code
#initialize
- #items
Returns the value of attribute items
+ show/hide code
- #total
Returns the value of attribute total
+ show/hide code
ActiveRel¶
Makes Neo4j Relationships more or less act like ActiveRecord objects. See documentation at https://github.com/neo4jrb/neo4j/wiki/Neo4j%3A%3AActiveRel
Types¶
provides mapping of type to model name
ClassMethods¶
#_type
#_wrapped_classes
#add_wrapped_class
#decorated_rel_type
#inherited
#namespaced_model_name
#rel_type
- #type
This option is used internally, users will usually ignore it.
+ show/hide code
Constants¶
- WRAPPED_CLASSES
Methods¶
Query¶
ClassMethods¶
- #all
Performs a basic match on the relationship, returning all results. This is not executed lazily, it will immediately return matching objects.
+ show/hide code
- #find
Returns the object with the specified neo4j id.
+ show/hide code
- #find_by_id
Loads the relationship using its neo_id.
+ show/hide code
#first
#last
- #where
Performs a very basic match on the relationship. This is not executed lazily, it will immediately return matching objects. To use a string, prefix the property with “r1”
+ show/hide code
Constants¶
Methods¶
Property¶
ClassMethods¶
#creates_unique_rel
#end_class
- #extract_association_attributes!
Extracts keys from attributes hash which are relationships of the model TODO: Validate separately that relationships are getting the right values? Perhaps also store the values and persist relationships on save?
+ show/hide code
#id_property_name
#load_entity
#start_class
#unique?
Constants¶
Methods¶
- #[]
Returning nil when we get ActiveAttr::UnknownAttributeError from ActiveAttr
+ show/hide code
- #_persisted_obj
Returns the value of attribute _persisted_obj
+ show/hide code
#default_properties
#default_properties=
#default_property
#end_node
#initialize
- #read_attribute
Returning nil when we get ActiveAttr::UnknownAttributeError from ActiveAttr
+ show/hide code
#send_props
#start_node
#type
Initialize¶
Constants¶
Methods¶
- #init_on_load
called when loading the rel from the database
+ show/hide code
- #wrapper
Implements the Neo4j::Node#wrapper and Neo4j::Relationship#wrapper method so that we don’t have to care if the node is wrapped or not.
+ show/hide code
Validations¶
Constants¶
Methods¶
- #read_attribute_for_validation
Implements the ActiveModel::Validation hook method.
+ show/hide code
- #save
The validation process on save can be skipped by passing false. The regular Model#save method is replaced with this when the validations module is mixed in, which it is by default.
+ show/hide code
#valid?
Persistence¶
ClassMethods¶
- #create
Creates a new relationship between objects
+ show/hide code
- #create!
Same as #create, but raises an error if there is a problem during save.
+ show/hide code
Constants¶
- N1_N2_STRING
- ACTIVEREL_NODE_MATCH_STRING
- USES_CLASSNAME
Methods¶
#_active_record_destroyed_behavior?
- #_destroyed_double_check?
These two methods should be removed in 6.0.0
+ show/hide code
#apply_default_values
- #association_proxy_cache
Should probably find a way to not need this
+ show/hide code
#cache_key
#create_model
#create_or_update
#destroy
- #destroyed?
Returns +true+ if the object was destroyed.
+ show/hide code
#exist?
#freeze
#frozen?
- #new?
Returns +true+ if the record hasn’t been saved to Neo4j yet.
+ show/hide code
- #new_record?
Returns +true+ if the record hasn’t been saved to Neo4j yet.
+ show/hide code
- #persisted?
Returns +true+ if the record is persisted, i.e. it’s not a new record and it was not destroyed
+ show/hide code
#props
#reload
#reload_from_database
#save
#save!
- #update
Updates this resource with all the attributes from the passed-in Hash and requests that the record be saved. If saving fails because the resource is invalid then false will be returned.
+ show/hide code
- #update!
Same as {#update_attributes}, but raises an exception if saving fails.
+ show/hide code
- #update_attribute
Convenience method to set attribute and #save at the same time
+ show/hide code
- #update_attribute!
Convenience method to set attribute and #save! at the same time
+ show/hide code
- #update_attributes
Updates this resource with all the attributes from the passed-in Hash and requests that the record be saved. If saving fails because the resource is invalid then false will be returned.
+ show/hide code
- #update_attributes!
Same as {#update_attributes}, but raises an exception if saving fails.
+ show/hide code
#update_model
Constants¶
- WRAPPED_CLASSES
- N1_N2_STRING
- ACTIVEREL_NODE_MATCH_STRING
- USES_CLASSNAME
Files¶
- lib/neo4j/active_rel.rb:4
- lib/neo4j/active_rel/types.rb:2
- lib/neo4j/active_rel/query.rb:1
- lib/neo4j/active_rel/property.rb:1
- lib/neo4j/active_rel/callbacks.rb:2
- lib/neo4j/active_rel/initialize.rb:1
- lib/neo4j/active_rel/validations.rb:2
- lib/neo4j/active_rel/persistence.rb:1
- lib/neo4j/active_rel/related_node.rb:1
Methods¶
#==
- #[]
Returning nil when we get ActiveAttr::UnknownAttributeError from ActiveAttr
+ show/hide code
#_active_record_destroyed_behavior?
- #_destroyed_double_check?
These two methods should be removed in 6.0.0
+ show/hide code
- #_persisted_obj
Returns the value of attribute _persisted_obj
+ show/hide code
#apply_default_values
- #association_proxy_cache
Should probably find a way to not need this
+ show/hide code
#cache_key
#declared_property_manager
#default_properties
#default_properties=
#default_property
- #destroy
nodoc:
- #destroyed?
Returns +true+ if the object was destroyed.
+ show/hide code
#end_node
#eql?
#exist?
#freeze
#frozen?
#hash
#id
- #init_on_load
called when loading the rel from the database
+ show/hide code
#initialize
#neo4j_obj
#neo_id
- #new?
Returns +true+ if the record hasn’t been saved to Neo4j yet.
+ show/hide code
- #new_record?
Returns +true+ if the record hasn’t been saved to Neo4j yet.
+ show/hide code
- #persisted?
Returns +true+ if the record is persisted, i.e. it’s not a new record and it was not destroyed
+ show/hide code
#props
- #read_attribute
Returning nil when we get ActiveAttr::UnknownAttributeError from ActiveAttr
+ show/hide code
- #read_attribute_for_validation
Implements the ActiveModel::Validation hook method.
+ show/hide code
#reload
#reload_from_database
#save
#save!
#send_props
#serializable_hash
#serialized_properties
#start_node
- #to_key
Returns an Enumerable of all (primary) key attributes or nil if model.persisted? is false
+ show/hide code
- #touch
nodoc:
#type
- #update
Updates this resource with all the attributes from the passed-in Hash and requests that the record be saved. If saving fails because the resource is invalid then false will be returned.
+ show/hide code
- #update!
Same as {#update_attributes}, but raises an exception if saving fails.
+ show/hide code
- #update_attribute
Convenience method to set attribute and #save at the same time
+ show/hide code
- #update_attribute!
Convenience method to set attribute and #save! at the same time
+ show/hide code
- #update_attributes
Updates this resource with all the attributes from the passed-in Hash and requests that the record be saved. If saving fails because the resource is invalid then false will be returned.
+ show/hide code
- #update_attributes!
Same as {#update_attributes}, but raises an exception if saving fails.
+ show/hide code
#valid?
- #wrapper
Implements the Neo4j::Node#wrapper and Neo4j::Relationship#wrapper method so that we don’t have to care if the node is wrapped or not.
+ show/hide code
Core¶
Query¶
Constants¶
Methods¶
- #proxy_as
Creates a Neo4j::ActiveNode::Query::QueryProxy object that builds off of a Core::Query object.
+ show/hide code
- #proxy_as_optional
Calls proxy_as with optional set true. This doesn’t offer anything different from calling proxy_as directly but it may be more readable.
+ show/hide code
- #proxy_chain_level
For instances where you turn a QueryProxy into a Query and then back to a QueryProxy with #proxy_as
+ show/hide code
- #proxy_chain_level=
For instances where you turn a QueryProxy into a Query and then back to a QueryProxy with #proxy_as
+ show/hide code
Constants¶
Methods¶
ActiveNode¶
Makes Neo4j nodes and relationships behave like ActiveRecord objects. By including this module in your class it will create a mapping for the node to your ruby class by using a Neo4j Label with the same name as the class. When the node is loaded from the database it will check if there is a ruby class for the labels it has. If there Ruby class with the same name as the label then the Neo4j node will be wrapped in a new object of that class.
= ClassMethods * {Neo4j::ActiveNode::Labels::ClassMethods} defines methods like: <tt>index</tt> and <tt>find</tt> * {Neo4j::ActiveNode::Persistence::ClassMethods} defines methods like: <tt>create</tt> and <tt>create!</tt> * {Neo4j::ActiveNode::Property::ClassMethods} defines methods like: <tt>property</tt>.
Query¶
Helper methods to return Neo4j::Core::Query objects. A query object can be used to successively build a cypher query
person.query_as(:n).match(‘n-[:friend]-o’).return(o: :name) # Return the names of all the person’s friends
ClassMethods¶
- #as
Start a new QueryProxy with the starting identifier set to the given argument. This method does not exist within QueryProxy, it can only be called at the class level to create a new QP object. To set an identifier within a QueryProxy chain, give it as the first argument to a chained association.
+ show/hide code
- #query_as
Returns a Query object with all nodes for the model matched as the specified variable name
an early Cypher match has already filtered results) where including labels will degrade performance.
+ show/hide code
#query_proxy
QueryProxy¶
#args
- #clause
Returns the value of attribute clause
+ show/hide code
.for_arg
.for_args
.for_association
.for_clause
.for_node_where_clause
.for_order_clause
- .for_rel_where_clause
We don’t accept strings here. If you want to use a string, just use where.
+ show/hide code
.for_where_clause
#initialize
- METHODS
- FIRST
- LAST
- #<<
To add a relationship for the node for the association on this QueryProxy
+ show/hide code
- #==
Does exactly what you would hope. Without it, comparing bobby.lessons == sandy.lessons would evaluate to false because it would be comparing the QueryProxy objects, not the lessons themselves.
+ show/hide code
#[]
#_create_relationship
- #_model_label_string
param [TrueClass, FalseClass] with_labels This param is used by certain QueryProxy methods that already have the neo_id and therefore do not need labels. The @association_labels instance var is set during init and used during association chaining to keep labels out of Cypher queries.
+ show/hide code
#_nodeify!
- #all_rels_to
Returns all relationships across a QueryProxy chain between a given node or array of nodes and the preceeding link.
+ show/hide code
- #as_models
Takes an Array of ActiveNode models and applies the appropriate WHERE clause So for a Teacher model inheriting from a Person model and an Article model if you called .as_models([Teacher, Article]) The where clause would look something like:
WHERE (node_var:Teacher:Person OR node_var:Article)
- #association
The most recent node to start a QueryProxy chain. Will be nil when using QueryProxy chains on class methods.
+ show/hide code
#base_query
#blank?
- #context
Returns the value of attribute context
+ show/hide code
#count
#create
- #delete
Deletes the relationship between a node and its last link in the QueryProxy chain. Executed in the database, callbacks will not run.
+ show/hide code
- #delete_all
Deletes a group of nodes and relationships within a QP chain. When identifier is omitted, it will remove the last link in the chain. The optional argument must be a node identifier. A relationship identifier will result in a Cypher Error
+ show/hide code
- #delete_all_rels
Deletes the relationships between all nodes for the last step in the QueryProxy chain. Executed in the database, callbacks will not be run.
+ show/hide code
- #destroy
Returns all relationships between a node and its last link in the QueryProxy chain, destroys them in Ruby. Callbacks will be run.
+ show/hide code
#each
- #each_for_destruction
Used as part of dependent: :destroy and may not have any utility otherwise. It keeps track of the node responsible for a cascading destroy process. but this is not always available, so we require it explicitly.
+ show/hide code
- #each_rel
When called at the end of a QueryProxy chain, it will return the resultant relationship objects intead of nodes. For example, to return the relationship between a given student and their lessons:
student.lessons.each_rel do |rel|
- #each_with_rel
When called at the end of a QueryProxy chain, it will return the nodes and relationships of the last link. For example, to return a lesson and each relationship to a given student:
student.lessons.each_with_rel do |lesson, rel|
#empty?
#exists?
- #find
Give ability to call #find on associations to get a scoped find Doesn’t pass through via method_missing because Enumerable has a #find method
+ show/hide code
#find_each
#find_in_batches
- #find_or_create_by
When called, this method returns a single node that satisfies the match specified in the params hash. If no existing node is found to satisfy the match, one is created or associated as expected.
+ show/hide code
#first
- #first_rel_to
Gives you the first relationship between the last link of a QueryProxy chain and a given node Shorthand for MATCH (start)-[r]-(other_node) WHERE ID(other_node) = #{other_node.neo_id} RETURN r
+ show/hide code
#identity
#include?
- #initialize
QueryProxy is ActiveNode’s Cypher DSL. While the name might imply that it creates queries in a general sense, it is actually referring to <tt>Neo4j::Core::Query</tt>, which is a pure Ruby Cypher DSL provided by the <tt>neo4j-core</tt> gem. QueryProxy provides ActiveRecord-like methods for common patterns. When it’s not handling CRUD for relationships and queries, it provides ActiveNode’s association chaining (student.lessons.teachers.where(age: 30).hobbies) and enjoys long walks on the beach.
It should not ever be necessary to instantiate a new QueryProxy object directly, it always happens as a result of calling a method that makes use of it.
originated. <tt>has_many</tt>) that created this object. * node_var: A string or symbol to be used by Cypher within its query string as an identifier * rel_var: Same as above but pertaining to a relationship identifier * session: The session to be used for this query * source_object: The node instance at the start of the QueryProxy chain * query_proxy: An existing QueryProxy chain upon which this new object should be built
QueryProxy objects are evaluated lazily.
+ show/hide code
#inspect
#last
#length
- #limit_value
TODO: update this with public API methods if/when they are exposed
+ show/hide code
- #match_to
Shorthand for MATCH (start)-[r]-(other_node) WHERE ID(other_node) = #{other_node.neo_id} The node param can be a persisted ActiveNode instance, any string or integer, or nil. When it’s a node, it’ll use the object’s neo_id, which is fastest. When not nil, it’ll figure out the primary key of that model. When nil, it uses 1 = 2 to prevent matching all records, which is the default behavior when nil is passed to where in QueryProxy.
+ show/hide code
- #method_missing
QueryProxy objects act as a representation of a model at the class level so we pass through calls This allows us to define class functions for reusable query chaining or for end-of-query aggregation/summarizing
+ show/hide code
- #model
The most recent node to start a QueryProxy chain. Will be nil when using QueryProxy chains on class methods.
+ show/hide code
#new_link
#node_identity
- #node_var
The current node identifier on deck, so to speak. It is the object that will be returned by calling each and the last node link in the QueryProxy chain.
+ show/hide code
- #node_where
Since there is a rel_where method, it seems only natural for there to be node_where
+ show/hide code
#offset
- #optional
A shortcut for attaching a new, optional match to the end of a QueryProxy chain.
+ show/hide code
#optional?
#order_by
#params
- #pluck
For getting variables which have been defined as part of the association chain
+ show/hide code
- #query
Like calling #query_as, but for when you don’t care about the variable name
+ show/hide code
- #query_as
Build a Neo4j::Core::Query object for the QueryProxy. This is necessary when you want to take an existing QueryProxy chain and work with it from the more powerful (but less friendly) Neo4j::Core::Query.
+ show/hide code
- #query_proxy
Returns the value of attribute query_proxy
+ show/hide code
#read_attribute_for_serialization
#rel
#rel_identity
- #rel_var
The relationship identifier most recently used by the QueryProxy chain.
+ show/hide code
#rels
- #rels_to
Returns all relationships across a QueryProxy chain between a given node or array of nodes and the preceeding link.
+ show/hide code
- #replace_with
Deletes the relationships between all nodes for the last step in the QueryProxy chain and replaces them with relationships to the given nodes. Executed in the database, callbacks will not be run.
+ show/hide code
#respond_to_missing?
- #scoping
Scope all queries to the current scope.
- Comment.where(post_id: 1).scoping do
- Comment.first
end
TODO: unscoped Please check unscoped if you want to remove all previous scopes (including the default_scope) during the execution of a block.
+ show/hide code
#size
- #source_object
The most recent node to start a QueryProxy chain. Will be nil when using QueryProxy chains on class methods.
+ show/hide code
- #start_object
Returns the value of attribute start_object
+ show/hide code
- #starting_query
The most recent node to start a QueryProxy chain. Will be nil when using QueryProxy chains on class methods.
+ show/hide code
- #to_cypher
Cypher string for the QueryProxy’s query. This will not include params. For the full output, see <tt>to_cypher_with_params</tt>.
+ show/hide code
- #to_cypher_with_params
Returns a string of the cypher query with return objects and params
+ show/hide code
- #unique_nodes
This will match nodes who only have a single relationship of a given type. It’s used by dependent: :delete_orphans and dependent: :destroy_orphans and may not have much utility otherwise.
+ show/hide code
#with_associations
QueryProxyMethods¶
- FIRST
- LAST
- #all_rels_to
Returns all relationships across a QueryProxy chain between a given node or array of nodes and the preceeding link.
+ show/hide code
- #as_models
Takes an Array of ActiveNode models and applies the appropriate WHERE clause So for a Teacher model inheriting from a Person model and an Article model if you called .as_models([Teacher, Article]) The where clause would look something like:
WHERE (node_var:Teacher:Person OR node_var:Article)
#blank?
#count
- #delete
Deletes the relationship between a node and its last link in the QueryProxy chain. Executed in the database, callbacks will not run.
+ show/hide code
- #delete_all
Deletes a group of nodes and relationships within a QP chain. When identifier is omitted, it will remove the last link in the chain. The optional argument must be a node identifier. A relationship identifier will result in a Cypher Error
+ show/hide code
- #delete_all_rels
Deletes the relationships between all nodes for the last step in the QueryProxy chain. Executed in the database, callbacks will not be run.
+ show/hide code
- #destroy
Returns all relationships between a node and its last link in the QueryProxy chain, destroys them in Ruby. Callbacks will be run.
+ show/hide code
#empty?
#exists?
- #find
Give ability to call #find on associations to get a scoped find Doesn’t pass through via method_missing because Enumerable has a #find method
+ show/hide code
- #find_or_create_by
When called, this method returns a single node that satisfies the match specified in the params hash. If no existing node is found to satisfy the match, one is created or associated as expected.
+ show/hide code
#first
- #first_rel_to
Gives you the first relationship between the last link of a QueryProxy chain and a given node Shorthand for MATCH (start)-[r]-(other_node) WHERE ID(other_node) = #{other_node.neo_id} RETURN r
+ show/hide code
#include?
#last
#length
- #limit_value
TODO: update this with public API methods if/when they are exposed
+ show/hide code
- #match_to
Shorthand for MATCH (start)-[r]-(other_node) WHERE ID(other_node) = #{other_node.neo_id} The node param can be a persisted ActiveNode instance, any string or integer, or nil. When it’s a node, it’ll use the object’s neo_id, which is fastest. When not nil, it’ll figure out the primary key of that model. When nil, it uses 1 = 2 to prevent matching all records, which is the default behavior when nil is passed to where in QueryProxy.
+ show/hide code
- #optional
A shortcut for attaching a new, optional match to the end of a QueryProxy chain.
+ show/hide code
#rel
#rels
- #rels_to
Returns all relationships across a QueryProxy chain between a given node or array of nodes and the preceeding link.
+ show/hide code
- #replace_with
Deletes the relationships between all nodes for the last step in the QueryProxy chain and replaces them with relationships to the given nodes. Executed in the database, callbacks will not be run.
+ show/hide code
#size
QueryProxyEnumerable¶
Methods related to returning nodes and rels from QueryProxy
- #==
Does exactly what you would hope. Without it, comparing bobby.lessons == sandy.lessons would evaluate to false because it would be comparing the QueryProxy objects, not the lessons themselves.
+ show/hide code
- #each
Just like every other <tt>each</tt> but it allows for optional params to support the versions that also return relationships. The <tt>node</tt> and <tt>rel</tt> params are typically used by those other methods but there’s nothing stopping you from using your_node.each(true, true) instead of your_node.each_with_rel.
+ show/hide code
- #each_rel
When called at the end of a QueryProxy chain, it will return the resultant relationship objects intead of nodes. For example, to return the relationship between a given student and their lessons:
student.lessons.each_rel do |rel|
- #each_with_rel
When called at the end of a QueryProxy chain, it will return the nodes and relationships of the last link. For example, to return a lesson and each relationship to a given student:
student.lessons.each_with_rel do |lesson, rel|
- #pluck
For getting variables which have been defined as part of the association chain
+ show/hide code
Constants¶
Files¶
- lib/neo4j/active_node/query.rb:7
- lib/neo4j/active_node/query/query_proxy.rb:3
- lib/neo4j/active_node/query/query_proxy_link.rb:3
- lib/neo4j/active_node/query/query_proxy_methods.rb:3
- lib/neo4j/active_node/query/query_proxy_enumerable.rb:3
- lib/neo4j/active_node/query/query_proxy_eager_loading.rb:3
- lib/neo4j/active_node/query/query_proxy_find_in_batches.rb:3
Methods¶
- #as
Starts a new QueryProxy with the starting identifier set to the given argument and QueryProxy source_object set to the node instance. This method does not exist within QueryProxy and can only be used to start a new chain.
+ show/hide code
- #query_as
Returns a Query object with the current node matched the specified variable name
+ show/hide code
HasN¶
AssociationProxy¶
Return this object from associations It uses a QueryProxy to get results But also caches results and can have results cached on it
- QUERY_PROXY_METHODS
- CACHED_RESULT_METHODS
#cache_query_proxy_result
#cache_result
#cached?
#clear_cache_result
#each
#initialize
- #inspect
States: Default
+ show/hide code
#method_missing
#result
ClassMethods¶
- #association?
rubocop:enable Style/PredicateName :nocov:
+ show/hide code
#associations
#associations_keys
- #has_association?
nocov: rubocop:disable Style/PredicateName
+ show/hide code
- #has_many
For defining an “has many” association on a model. This defines a set of methods on your model instances. For instance, if you define the association on a Person model:
has_many :out, :vehicles, type: :has_vehicle
This would define the following methods:
- #vehicles
- Returns a QueryProxy object. This is an Enumerable object and thus can be iterated over. It also has the ability to accept class-level methods from the Vehicle model (including calls to association methods)
- #vehicles=
- Takes an array of Vehicle objects and replaces all current
:HAS_VEHICLE
relationships with new relationships refering to the specified objects - .vehicles
Returns a QueryProxy object. This would represent all
Vehicle
objects associated with either allPerson
nodes (ifPerson.vehicles
is called), or allVehicle
objects associated with thePerson
nodes thus far represented in the QueryProxy chain. For example:company.people.where(age: 40).vehicles
- Arguments:
- direction:
Available values:
:in
,:out
, or:both
.Refers to the relative to the model on which the association is being defined.
- Example:
Person.has_many :out, :posts, type: :wrote
means that a WROTE relationship goes from a Person node to a Post node
- name:
The name of the association. The affects the methods which are created (see above). The name is also used to form default assumptions about the model which is being referred to
- Example:
Person.has_many :out, :posts, type: :wrote
will assume a model_class option of
'Post'
unless otherwise specified
- options: A
Hash
of options. Allowed keys are: - type: The Neo4j relationship type. This option is required unless either the
- origin or rel_class options are specified
- origin: The name of the association from another model which the type and model_class
can be gathered.
- Example:
Person.has_many :out, :posts, origin: :author
(model_class of Post is assumed here)Post.has_one :in, :author, type: :has_author, model_class: 'Person'
- model_class: The model class to which the association is referring. Can be either a
- model object
include
ingActiveNode
or a string (or anArray
of same). A string is recommended to avoid load-time issues - rel_class: The
ActiveRel
class to use for this association. Can be either a - model object
include
ingActiveRel
or a string (or anArray
of same). A string is recommended to avoid load-time issues - dependent: Enables deletion cascading.
- Available values:
:delete
,:delete_orphans
,:destroy
,:destroy_orphans
(note that the:destroy_orphans
option is known to be “very metal”. Caution advised)
- #has_one
For defining an “has one” association on a model. This defines a set of methods on your model instances. For instance, if you define the association on a Person model:
has_one :out, :vehicle, type: :has_vehicle
This would define the methods:
#vehicle
,#vehicle=
, and.vehicle
.See #has_many for anything not specified here
+ show/hide code
- #inherited
make sure the inherited classes inherit the <tt>_decl_rels</tt> hash
+ show/hide code
Association¶
- VALID_ASSOCIATION_OPTION_KEYS
#add_destroy_callbacks
- #arrow_cypher
Return cypher partial query string for the relationship part of a MATCH (arrow / relationship definition)
+ show/hide code
#callback
#create_method
#decorated_rel_type
- #dependent
Returns the value of attribute dependent
+ show/hide code
#derive_model_class
- #direction
Returns the value of attribute direction
+ show/hide code
#discovered_model
#initialize
#inject_classname
- #model_class
Returns the value of attribute model_class
+ show/hide code
- #name
Returns the value of attribute name
+ show/hide code
#perform_callback
- #relationship
Returns the value of attribute relationship
+ show/hide code
#relationship_class
- #relationship_class_name
Returns the value of attribute relationship_class_name
+ show/hide code
#relationship_class_type
#relationship_type
#target_class
#target_class_names
#target_class_option
#target_classes
#target_classes_or_nil
#target_where_clause
- #type
Returns the value of attribute type
+ show/hide code
#unique?
#validate_dependent
Constants¶
Methods¶
#association_proxy
- #association_proxy_cache
Returns the current AssociationProxy cache for the association cache. It is in the format { :association_name => AssociationProxy} This is so that we * don’t need to re-build the QueryProxy objects * also because the QueryProxy object caches it’s results * so we don’t need to query again * so that we can cache results from association calls or eager loading
+ show/hide code
#association_proxy_cache_fetch
#association_query_proxy
Scope¶
ClassMethods¶
#_call_scope_context
#_scope
#all
- #current_scope
nodoc:
- #current_scope=
nodoc:
- #has_scope?
rubocop:disable Style/PredicateName
+ show/hide code
- #scope
Similar to ActiveRecord scope
+ show/hide code
- #scope?
rubocop:enable Style/PredicateName
+ show/hide code
ScopeRegistry¶
Stolen from ActiveRecord https://github.com/rails/rails/blob/08754f12e65a9ec79633a605e986d0f1ffa4b251/activerecord/lib/active_record/scoping.rb#L57
- VALID_SCOPE_TYPES
#initialize
- #set_value_for
Sets the +value+ for a given +scope_type+ and +variable_name+.
+ show/hide code
- #value_for
Obtains the value for a given +scope_name+ and +variable_name+.
+ show/hide code
Constants¶
Methods¶
Labels¶
Provides a mapping between neo4j labels and Ruby classes
ClassMethods¶
#base_class
#blank?
- #constraint
Creates a neo4j constraint on this class for given property
+ show/hide code
#count
- #delete_all
Deletes all nodes and connected relationships from Cypher.
+ show/hide code
- #destroy_all
Returns each node to Ruby and calls destroy. Be careful, as this can be a very slow operation if you have many nodes. It will generate at least one database query per node in the database, more if callbacks require them.
+ show/hide code
#drop_constraint
#empty?
#exists?
- #find
Returns the object with the specified neo4j id.
+ show/hide code
- #find_by
Finds the first record matching the specified conditions. There is no implied ordering so if order matters, you should specify it yourself.
+ show/hide code
- #find_by!
Like find_by, except that if no record is found, raises a RecordNotFound error.
+ show/hide code
#find_each
#find_in_batches
- #first
Returns the first node of this class, sorted by ID. Note that this may not be the first node created since Neo4j recycles IDs.
+ show/hide code
- #index
Creates a Neo4j index on given property
This can also be done on the property directly, see Neo4j::ActiveNode::Property::ClassMethods#property.
+ show/hide code
#index?
#indexed_properties
- #last
Returns the last node of this class, sorted by ID. Note that this may not be the first node created since Neo4j recycles IDs.
+ show/hide code
#length
#mapped_label
#mapped_label_name
#mapped_label_names
#size
Constants¶
- WRAPPED_CLASSES
- MODELS_FOR_LABELS_CACHE
Methods¶
._wrapped_classes
- #add_label
adds one or more labels
+ show/hide code
.add_wrapped_class
.clear_model_for_label_cache
.clear_wrapped_models
#labels
.model_cache
.model_for_labels
- #remove_label
Removes one or more labels Be careful, don’t remove the label representing the Ruby class.
+ show/hide code
Property¶
ClassMethods¶
- #extract_association_attributes!
Extracts keys from attributes hash which are associations of the model TODO: Validate separately that relationships are getting the right values? Perhaps also store the values and persist relationships on save?
+ show/hide code
Constants¶
Methods¶
- #[]
Returning nil when we get ActiveAttr::UnknownAttributeError from ActiveAttr
+ show/hide code
- #_persisted_obj
Returns the value of attribute _persisted_obj
+ show/hide code
#default_properties
#default_properties=
#default_property
#initialize
- #read_attribute
Returning nil when we get ActiveAttr::UnknownAttributeError from ActiveAttr
+ show/hide code
#send_props
Dependent¶
QueryProxyMethods¶
methods used to resolve association dependencies
- #each_for_destruction
Used as part of dependent: :destroy and may not have any utility otherwise. It keeps track of the node responsible for a cascading destroy process. but this is not always available, so we require it explicitly.
+ show/hide code
- #unique_nodes
This will match nodes who only have a single relationship of a given type. It’s used by dependent: :delete_orphans and dependent: :destroy_orphans and may not have much utility otherwise.
+ show/hide code
Constants¶
Files¶
Reflection¶
A reflection contains information about an association. They are often used in connection with form builders to determine associated classes. This module contains methods related to the creation and retrieval of reflections.
ClassMethods¶
Adds methods to the class related to creating and retrieving reflections.
- #reflect_on_all_associations
Returns an array containing one reflection for each association declared in the model.
+ show/hide code
#reflect_on_association
AssociationReflection¶
The actual reflection object that contains information about the given association. These should never need to be created manually, they will always be created by declaring a :has_many or :has_one association on a model.
- #association
The association object referenced by this reflection
+ show/hide code
- #class_name
Returns the name of the target model
+ show/hide code
#collection?
#initialize
- #klass
Returns the target model
+ show/hide code
- #macro
The type of association
+ show/hide code
- #name
The name of the association
+ show/hide code
#rel_class_name
#rel_klass
#type
#validate?
Constants¶
Methods¶
Initialize¶
Constants¶
Methods¶
- #called_by
Returns the value of attribute called_by
+ show/hide code
- #init_on_load
called when loading the node from the database
+ show/hide code
- #wrapper
Implements the Neo4j::Node#wrapper and Neo4j::Relationship#wrapper method so that we don’t have to care if the node is wrapped or not.
+ show/hide code
Persistence¶
RecordInvalidError¶
ClassMethods¶
- #create
Creates and saves a new node
+ show/hide code
- #create!
Same as #create, but raises an error if there is a problem during save.
+ show/hide code
#find_or_create
- #find_or_create_by
Finds the first node with the given attributes, or calls create if none found
+ show/hide code
- #find_or_create_by!
Same as #find_or_create_by, but calls #create! so it raises an error if there is a problem during save.
+ show/hide code
#load_entity
#merge
Constants¶
- USES_CLASSNAME
Methods¶
#_active_record_destroyed_behavior?
#_create_node
- #_destroyed_double_check?
These two methods should be removed in 6.0.0
+ show/hide code
#apply_default_values
#cache_key
- #create_model
Creates a model with values matching those of the instance attributes and returns its id.
+ show/hide code
#create_or_update
#destroy
- #destroyed?
Returns +true+ if the object was destroyed.
+ show/hide code
#exist?
#freeze
#frozen?
- #new?
Returns +true+ if the record hasn’t been saved to Neo4j yet.
+ show/hide code
- #new_record?
Returns +true+ if the record hasn’t been saved to Neo4j yet.
+ show/hide code
- #persisted?
Returns +true+ if the record is persisted, i.e. it’s not a new record and it was not destroyed
+ show/hide code
#props
#reload
#reload_from_database
- #save
Saves the model.
If the model is new a record gets created in the database, otherwise the existing record gets updated. If perform_validation is true validations run. If any of them fail the action is cancelled and save returns false. If the flag is false validations are bypassed altogether. See ActiveRecord::Validations for more information. There’s a series of callbacks associated with save. If any of the before_* callbacks return false the action is cancelled and save returns false.
+ show/hide code
- #save!
Persist the object to the database. Validations and Callbacks are included by default but validation can be disabled by passing :validate => false to #save! Creates a new transaction.
+ show/hide code
- #update
Updates this resource with all the attributes from the passed-in Hash and requests that the record be saved. If saving fails because the resource is invalid then false will be returned.
+ show/hide code
- #update!
Same as {#update_attributes}, but raises an exception if saving fails.
+ show/hide code
- #update_attribute
Convenience method to set attribute and #save at the same time
+ show/hide code
- #update_attribute!
Convenience method to set attribute and #save! at the same time
+ show/hide code
- #update_attributes
Updates this resource with all the attributes from the passed-in Hash and requests that the record be saved. If saving fails because the resource is invalid then false will be returned.
+ show/hide code
- #update_attributes!
Same as {#update_attributes}, but raises an exception if saving fails.
+ show/hide code
#update_model
IdProperty¶
This module makes it possible to use other IDs than the build it neo4j id (neo_id)
ClassMethods¶
#find_by_id
#find_by_ids
#find_by_neo_id
- #has_id_property?
rubocop:disable Style/PredicateName
+ show/hide code
#id_property
- #id_property?
rubocop:enable Style/PredicateName
+ show/hide code
#id_property_info
#id_property_name
#primary_key
Constants¶
Methods¶
Validations¶
This mixin replace the original save method and performs validation before the save.
Constants¶
Methods¶
- #read_attribute_for_validation
Implements the ActiveModel::Validation hook method.
+ show/hide code
- #save
The validation process on save can be skipped by passing false. The regular Model#save method is replaced with this when the validations module is mixed in, which it is by default.
+ show/hide code
#valid?
OrmAdapter¶
Constants¶
Methods¶
#column_names
- #create!
Create a model using attributes
+ show/hide code
#destroy
- #find_all
Find all models matching conditions
+ show/hide code
- #find_first
Find the first instance matching conditions
+ show/hide code
- #get
Get an instance by id of the model
+ show/hide code
- #get!
Get an instance by id of the model
+ show/hide code
#i18n_scope
QueryMethods¶
Constants¶
Methods¶
#blank?
#count
#empty?
#exists?
#find_each
#find_in_batches
- #first
Returns the first node of this class, sorted by ID. Note that this may not be the first node created since Neo4j recycles IDs.
+ show/hide code
- #last
Returns the last node of this class, sorted by ID. Note that this may not be the first node created since Neo4j recycles IDs.
+ show/hide code
#length
#size
Constants¶
- WRAPPED_CLASSES
- MODELS_FOR_LABELS_CACHE
- USES_CLASSNAME
Files¶
- lib/neo4j/active_node.rb:23
- lib/neo4j/active_node/rels.rb:1
- lib/neo4j/active_node/query.rb:2
- lib/neo4j/active_node/has_n.rb:1
- lib/neo4j/active_node/scope.rb:3
- lib/neo4j/active_node/labels.rb:2
- lib/neo4j/active_node/property.rb:1
- lib/neo4j/active_node/callbacks.rb:2
- lib/neo4j/active_node/dependent.rb:2
- lib/neo4j/active_node/reflection.rb:1
- lib/neo4j/active_node/persistence.rb:1
- lib/neo4j/active_node/id_property.rb:1
- lib/neo4j/active_node/validations.rb:2
- lib/neo4j/active_node/orm_adapter.rb:4
- lib/neo4j/active_node/query_methods.rb:2
- lib/neo4j/active_node/query/query_proxy.rb:2
- lib/neo4j/active_node/has_n/association.rb:4
- lib/neo4j/active_node/query/query_proxy_link.rb:2
- lib/neo4j/active_node/query/query_proxy_methods.rb:2
- lib/neo4j/active_node/query/query_proxy_enumerable.rb:2
- lib/neo4j/active_node/dependent/query_proxy_methods.rb:2
- lib/neo4j/active_node/dependent/association_methods.rb:2
- lib/neo4j/active_node/query/query_proxy_eager_loading.rb:2
- lib/neo4j/active_node/query/query_proxy_find_in_batches.rb:2
Methods¶
#==
- #[]
Returning nil when we get ActiveAttr::UnknownAttributeError from ActiveAttr
+ show/hide code
#_active_record_destroyed_behavior?
#_create_node
- #_destroyed_double_check?
These two methods should be removed in 6.0.0
+ show/hide code
- #_persisted_obj
Returns the value of attribute _persisted_obj
+ show/hide code
#_rels_delegator
- #add_label
adds one or more labels
+ show/hide code
#apply_default_values
- #as
Starts a new QueryProxy with the starting identifier set to the given argument and QueryProxy source_object set to the node instance. This method does not exist within QueryProxy and can only be used to start a new chain.
+ show/hide code
#association_proxy
- #association_proxy_cache
Returns the current AssociationProxy cache for the association cache. It is in the format { :association_name => AssociationProxy} This is so that we * don’t need to re-build the QueryProxy objects * also because the QueryProxy object caches it’s results * so we don’t need to query again * so that we can cache results from association calls or eager loading
+ show/hide code
#association_proxy_cache_fetch
#association_query_proxy
#cache_key
- #called_by
Returns the value of attribute called_by
+ show/hide code
- #called_by=
Sets the attribute called_by
+ show/hide code
#declared_property_manager
#default_properties
#default_properties=
#default_property
#dependent_children
- #destroy
nodoc:
- #destroyed?
Returns +true+ if the object was destroyed.
+ show/hide code
#eql?
#exist?
#freeze
#frozen?
#hash
#id
- #init_on_load
called when loading the node from the database
+ show/hide code
#initialize
#inspect
#labels
#neo4j_obj
#neo_id
- #new?
Returns +true+ if the record hasn’t been saved to Neo4j yet.
+ show/hide code
- #new_record?
Returns +true+ if the record hasn’t been saved to Neo4j yet.
+ show/hide code
- #persisted?
Returns +true+ if the record is persisted, i.e. it’s not a new record and it was not destroyed
+ show/hide code
#props
- #query_as
Returns a Query object with the current node matched the specified variable name
+ show/hide code
- #read_attribute
Returning nil when we get ActiveAttr::UnknownAttributeError from ActiveAttr
+ show/hide code
- #read_attribute_for_validation
Implements the ActiveModel::Validation hook method.
+ show/hide code
#reload
#reload_from_database
- #remove_label
Removes one or more labels Be careful, don’t remove the label representing the Ruby class.
+ show/hide code
- #save
The validation process on save can be skipped by passing false. The regular Model#save method is replaced with this when the validations module is mixed in, which it is by default.
+ show/hide code
- #save!
Persist the object to the database. Validations and Callbacks are included by default but validation can be disabled by passing :validate => false to #save! Creates a new transaction.
+ show/hide code
#send_props
#serializable_hash
#serialized_properties
- #to_key
Returns an Enumerable of all (primary) key attributes or nil if model.persisted? is false
+ show/hide code
- #touch
nodoc:
- #update
Updates this resource with all the attributes from the passed-in Hash and requests that the record be saved. If saving fails because the resource is invalid then false will be returned.
+ show/hide code
- #update!
Same as {#update_attributes}, but raises an exception if saving fails.
+ show/hide code
- #update_attribute
Convenience method to set attribute and #save at the same time
+ show/hide code
- #update_attribute!
Convenience method to set attribute and #save! at the same time
+ show/hide code
- #update_attributes
Updates this resource with all the attributes from the passed-in Hash and requests that the record be saved. If saving fails because the resource is invalid then false will be returned.
+ show/hide code
- #update_attributes!
Same as {#update_attributes}, but raises an exception if saving fails.
+ show/hide code
#valid?
- #wrapper
Implements the Neo4j::Node#wrapper and Neo4j::Relationship#wrapper method so that we don’t have to care if the node is wrapped or not.
+ show/hide code
Node¶
Wrapper¶
The wrapping process is what transforms a raw CypherNode or EmbeddedNode from Neo4j::Core into a healthy ActiveNode (or ActiveRel) object.
Constants¶
- CONSTANTS_FOR_LABELS_CACHE
Methods¶
#class_to_wrap
- #wrapper
this is a plugin in the neo4j-core so that the Ruby wrapper will be wrapped around the Neo4j::Node objects
+ show/hide code
Constants¶
Methods¶
Constants¶
- VERSION
Files¶
- lib/neo4j/config.rb:1
- lib/neo4j/shared.rb:1
- lib/neo4j/errors.rb:1
- lib/neo4j/version.rb:1
- lib/neo4j/railtie.rb:4
- lib/neo4j/wrapper.rb:1
- lib/neo4j/migration.rb:3
- lib/neo4j/paginated.rb:1
- lib/neo4j/active_rel.rb:1
- lib/neo4j/active_node.rb:1
- lib/neo4j/type_converters.rb:1
- lib/neo4j/active_rel/types.rb:1
- lib/neo4j/shared/callbacks.rb:1
- lib/neo4j/active_node/query.rb:1
- lib/neo4j/shared/typecaster.rb:1
- lib/neo4j/active_node/labels.rb:1
- lib/neo4j/shared/validations.rb:1
- lib/neo4j/active_rel/callbacks.rb:1
- lib/neo4j/active_node/callbacks.rb:1
- lib/neo4j/active_node/dependent.rb:1
- lib/neo4j/active_rel/validations.rb:1
- lib/neo4j/active_node/validations.rb:1
- lib/neo4j/active_node/orm_adapter.rb:3
- lib/neo4j/active_node/query_methods.rb:1
- lib/rails/generators/neo4j_generator.rb:5
- lib/neo4j/active_node/query/query_proxy.rb:1
- lib/neo4j/active_node/has_n/association.rb:3
- lib/neo4j/active_node/query/query_proxy_link.rb:1
- lib/neo4j/active_node/query/query_proxy_methods.rb:1
- lib/neo4j/active_node/query/query_proxy_enumerable.rb:1
- lib/neo4j/active_node/dependent/query_proxy_methods.rb:1
- lib/neo4j/active_node/dependent/association_methods.rb:1
- lib/neo4j/active_node/query/query_proxy_eager_loading.rb:1
- lib/neo4j/active_node/query/query_proxy_find_in_batches.rb:1
Methods¶
Neo4j.rb (the neo4j and neo4j-core gems) is a Ruby Object-Graph-Mapper (OGM) for the Neo4j graph database. It tries to follow API conventions established by ActiveRecord and familiar to most Ruby developers but with a Neo4j flavor.
- Ruby
- (software) A dynamic, open source programming language with a focus on simplicity and productivity. It has an elegant syntax that is natural to read and easy to write.
- Graph Database
- (computer science) A graph database stores data in a graph, the most generic of data structures, capable of elegantly representing any kind of data in a highly accessible way.
- Neo4j
- (databases) The world’s leading graph database
If you’re already familiar with ActiveRecord, DataMapper, or Mongoid, you’ll find the Object Model features you’ve come to expect from an O*M:
- Properties
- Indexes / Constraints
- Callbacks
- Validation
- Assocations
Because relationships are first-class citizens in Neo4j, models can be created for both nodes and relationships.
Additional features include¶
- A chainable arel-inspired query builder
- Transactions
- Migration framework
Requirements¶
- Ruby 1.9.3+ (tested in MRI and JRuby)
- Neo4j 2.1.0 + (version 4.0+ of the gem is required to use neo4j 2.2+)