-
Notifications
You must be signed in to change notification settings - Fork 276
Neo4j.rb v4 Introduction
As of November 21, 2014, we began merging code for v4 of the gem into the master branch. Unlike the jump from v2 to v3, this is not a complete rebuild. Most of v3's documentation is still valid; however, some subtle but potentially significant API changes have occurred that may be surprising if installed accidentally.
The changes are:
November 27, 2014
- You can now call custom methods and scopes within a QueryProxy chain. This works with both class and instance methods.
- Method improvements:
match_to
andfirst_rel_to
both accept strings representing IDs in additional to full nodes. -
rel_where
was added to QueryProxy. Like standardwhere
(which is also aliased tonode_where
for consistency), it will target the most recent link in the QP chain but it uses the last relationship, not node, and makes it easier to generate matches. - We are using ActiveSupport's load hooks with names
:active_node
and:active_rel
.
November 21, 2014
- _classname is no longer required for Neo4j 2.1.5+ unless explicitly added to a model.
- Relationships, when loaded, will attempt to look for a camelcased version of the relationship type.
- Relationship types, when allowed to be set automatically by the gem, will default to all caps with underscores. There is a global config option that will change this.
A guide for this is below. If any of this is unclear, please do not hesitate to create an issue or reach out to Chris or Brian through email. We have a discussion issue here.
In versions 2 and 3 of this gem, there was a _classname
property on each node and relationship created by a model. This was necessary in 2.x because Neo4j 1.9 did not have labels and 3.x because labels required extra work that often resulted in n+1 queries against the database. With Neo4j 2.1.5, released a few weeks after Neo4jrb 3.0 was officially rolled out, node labels and relationship types became part of every node/rel return and we were given a better option.
In Neo4jrb 4.0, the _classname
property only exists for legacy support and as an option if you want to override defaults. We call the process of turning a returned Neo4j db object into a usable model as "wrapping." Below, we'll give an overview of how the logic used by the gem to determine which model is responsible for an object. It is far from an exhaustive technical analysis but the code is pretty clear, so please check that or get in touch if you have any questions.
These would be legacy objects or objects that have their defaults modified. These will always be wrapped by whatever model is given as the property's value.
If a node does not have a _classname
property, the gem uses the node's labels to determine the model to use. This is handled in node_wrapper.rb and also makes use of labels.rb.
Use the set_mapped_label_name
method within your model to redefine the label used by the model. This is also defined in labels.rb
, linked above.
Relationships are mapped to models in a slightly more complicated process. This is necessary because a relationship can only have one type, so we have to provide extra configuration options to come close to the flexibility offered by multiple labels and subclasses.
The wrapping process is started in rel_wrapper.rb but a lot of the logic lives in types.rb. [Note: If that link stops working, it has probably be merged into master and that branch has been deleted. Please adjust the URL to use blog/master/lib
.)
First, it looks at the relationship's type and tries to find a matching key in the WRAPPED_CLASSES
constant. What happens next depends on what it finds.
- If a match is found, it uses uses the model name given as the value.
- If a match is not found, it attempts to load a model named after the camelized version of the relationship type.
HAS_LESSON
looks forHasLesson
,PLAYING_SHOW
becomesPlayingShow
, etc,... - If no match is found, it returns an unwrapped CypherRelationship or EmbeddedRelationship object based on the database type, as defined in
Neo4j::Core
.
You have two options. The first is to use the type
class method within a relationship model.
class StudentLesson
include Neo4j::ActiveRel
from_class Student
to_class Lesson
type 'ENROLLED_IN'
end
This will create relationships with type ENROLLED_IN
and ensure that any relationships returned with the ENROLLED_IN
type will be wrapped as StudentLesson.
The second option is to use set_classname
to intercept the object during its return and wrap it as a class. This is useful if you want to share one relationship type between multiple models. (Which may be a questionable practice but it has its place!)
class Band
include Neo4j::ActiveNode
has_many :out, :shows, rel_class: BandShow
has_many :out, :tours, rel_class: BandTour
has_many :out, events: model_class: false, type: 'PERFORMING_ON'
end
class BandShow
include Neo4j::ActiveRel
from_class Band
to_class Show
type 'PERFORMING_ON'
set_classname
end
class BandTour
include Neo4j::ActiveRel
from_class Band
to_class Tour
type `PERFORMING_ON`
set_classname
end
rel1 = BandShow.create(from_node: band, to_node: show)
rel2 = BandTour.create(from_node: band, to_node: tour)
band.events.count
# 2
band.events.each_rel do |r|
puts r.class
end
# BandShow(props...)
# BandTour(props...)
ActiveNode and ActiveRel models register themselves through type
or set_classname
when the models are loaded into memory. In Rails's development and test environments, models are loaded lazily, so it's possible to perform a query that results in returned objects that do not have models matching their labels or relationship types.
To get around this, ensure that you call each model once after Rails loads but before you perform your first queries or enable eager_loading
of your environment. You'll find this in your environment's config file, config.eager_load
.
In v3, automatic naming of relationships was downcase with underscores and #
prepended. If you had this:
has_many :out, :lessons_taught, model_class: 'Lesson'
...your relationship type would be #lessons_taught
. In v4, the relationship type will be LESSONS_TAUGHT
.
The reason we decided to do things as they were done in v3 was because we felt that it would be beneficial to tag relationships when they were created by the gem. If a user looked at their graph through the Neo4j web browser and came across a relationship that they didn't remember creating, it would hopefully remind them that they did not create the type.
In v4, we made the decision to go all caps and to not tag with #
because we want to encourage best practices. All Neo4j documentation and tutorials demonstrate relationship types with caps. We believe the gem should provide predictable defaults and, with that in mind, felt that the most predictable relationship type would be that which followed this widely used standard.
With all that said, we know that not everybody will want to do things this way. This is especially true if someone has a large database with legacy data that uses #old_format
relationship types. To address this, there is a new global config option: config.neo4j.transform_rel_type
. Used in application.rb
or wherever you normally specify your config info, it accepts three four options:
-
:upcase
-:this_class
,ThisClass
,thiS_claSs
(if you don't like yourself) becomesTHIS_CLASS
-
:downcase
- same as above, only... downcased. -
:legacy
- downcases and prepends#
, so ThisClass becomes#this_class
-
:none
- uses the string version of whatever is passed with no modifications
If you want to set this from within the app, you can use Neo4j::Config[:transform_rel_type] = #{your_sym}
.
These options apply to both associations in ActiveNode models and ActiveRel models. Since ActiveRel models no longer require type
, if you have enrolled_in.rb
with class EnrolledIn
, it will automatically create a relationship type of ENROLLED_IN
based on the default settings.
See the section dealing with _classname for more information about overriding types.
WARNING: Much of the information in this wiki is out of date. We are in the process of moving things to readthedocs
- Project Introduction
- Neo4j::ActiveNode
- Neo4j::ActiveRel
- Search and Scope
- Validation, Uniqueness, and Case Sensitivity
- Indexing VS Legacy Indexing
- Optimized Methods
- Inheritance
- Core: Nodes & Rels
- Introduction
- Persistence
- Find : Lucene
- Relationships
- Third Party Gems & extensions
- Scaffolding & Generators
- HA Cluster