neo4j 5.0.15 → 5.1.0.rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +60 -5
  3. data/Gemfile +1 -1
  4. data/README.md +8 -0
  5. data/lib/neo4j.rb +4 -0
  6. data/lib/neo4j/active_node.rb +3 -1
  7. data/lib/neo4j/active_node/dependent/association_methods.rb +4 -2
  8. data/lib/neo4j/active_node/dependent/query_proxy_methods.rb +3 -3
  9. data/lib/neo4j/active_node/has_n.rb +103 -36
  10. data/lib/neo4j/active_node/has_n/association.rb +10 -33
  11. data/lib/neo4j/active_node/has_n/association_cypher_methods.rb +108 -0
  12. data/lib/neo4j/active_node/id_property.rb +19 -11
  13. data/lib/neo4j/active_node/id_property/accessor.rb +62 -0
  14. data/lib/neo4j/active_node/labels.rb +13 -2
  15. data/lib/neo4j/active_node/persistence.rb +19 -4
  16. data/lib/neo4j/active_node/property.rb +4 -3
  17. data/lib/neo4j/active_node/query/query_proxy.rb +29 -13
  18. data/lib/neo4j/active_node/query/query_proxy_eager_loading.rb +8 -0
  19. data/lib/neo4j/active_node/query/query_proxy_enumerable.rb +7 -0
  20. data/lib/neo4j/active_node/query/query_proxy_link.rb +16 -6
  21. data/lib/neo4j/active_node/query/query_proxy_methods.rb +4 -0
  22. data/lib/neo4j/active_node/query/query_proxy_unpersisted.rb +17 -0
  23. data/lib/neo4j/active_node/unpersisted.rb +49 -0
  24. data/lib/neo4j/active_node/validations.rb +1 -1
  25. data/lib/neo4j/active_rel.rb +17 -0
  26. data/lib/neo4j/active_rel/persistence.rb +10 -5
  27. data/lib/neo4j/active_rel/property.rb +17 -5
  28. data/lib/neo4j/railtie.rb +2 -1
  29. data/lib/neo4j/shared/declared_property_manager.rb +10 -0
  30. data/lib/neo4j/shared/initialize.rb +3 -3
  31. data/lib/neo4j/shared/property.rb +7 -51
  32. data/lib/neo4j/shared/property/default_property.rb +0 -0
  33. data/lib/neo4j/shared/type_converters.rb +49 -6
  34. data/lib/neo4j/shared/typecaster.rb +22 -18
  35. data/lib/neo4j/shared/validations.rb +1 -1
  36. data/lib/neo4j/version.rb +1 -1
  37. data/neo4j.gemspec +1 -1
  38. metadata +12 -7
@@ -6,6 +6,7 @@ module Neo4j
6
6
  class Association
7
7
  include Neo4j::Shared::RelTypeConverters
8
8
  include Neo4j::ActiveNode::Dependent::AssociationMethods
9
+ include Neo4j::ActiveNode::HasN::AssociationCypherMethods
9
10
 
10
11
  attr_reader :type, :name, :relationship, :direction, :dependent, :model_class
11
12
 
@@ -29,7 +30,9 @@ module Neo4j
29
30
 
30
31
  def refresh_model_class!
31
32
  @pending_model_refresh = @target_classes_or_nil = nil
32
- @model_class = @model_class.name.constantize if @model_class
33
+
34
+ # Using #to_s on purpose here to take care of classes/strings/symbols
35
+ @model_class = @model_class.to_s.constantize if @model_class
33
36
  end
34
37
 
35
38
  def queue_model_refresh!
@@ -49,12 +52,6 @@ module Neo4j
49
52
  end
50
53
  end
51
54
 
52
- # Return cypher partial query string for the relationship part of a MATCH (arrow / relationship definition)
53
- def arrow_cypher(var = nil, properties = {}, create = false, reverse = false)
54
- validate_origin!
55
- direction_cypher(get_relationship_cypher(var, properties, create), create, reverse)
56
- end
57
-
58
55
  def pending_model_refresh?
59
56
  !!@pending_model_refresh
60
57
  end
@@ -110,9 +107,12 @@ module Neo4j
110
107
 
111
108
  def relationship_type(create = false)
112
109
  case
113
- when relationship_class then relationship_class_type
114
- when @relationship_type then @relationship_type
115
- when @origin then origin_type
110
+ when relationship_class
111
+ relationship_class_type
112
+ when @relationship_type
113
+ @relationship_type
114
+ when @origin
115
+ origin_type
116
116
  else
117
117
  (create || exceptional_target_class?) && decorated_rel_type(@name)
118
118
  end
@@ -148,14 +148,6 @@ module Neo4j
148
148
  Neo4j::Config.association_model_namespace_string
149
149
  end
150
150
 
151
- def direction_cypher(relationship_cypher, create, reverse = false)
152
- case get_direction(create, reverse)
153
- when :out then "-#{relationship_cypher}->"
154
- when :in then "<-#{relationship_cypher}-"
155
- when :both then "-#{relationship_cypher}-"
156
- end
157
- end
158
-
159
151
  def get_direction(create, reverse = false)
160
152
  dir = (create && @direction == :both) ? :out : @direction
161
153
  if reverse
@@ -169,21 +161,6 @@ module Neo4j
169
161
  end
170
162
  end
171
163
 
172
- def get_relationship_cypher(var, properties, create)
173
- relationship_type = relationship_type(create)
174
- relationship_name_cypher = ":`#{relationship_type}`" if relationship_type
175
- properties_string = get_properties_string(properties)
176
-
177
- "[#{var}#{relationship_name_cypher}#{properties_string}]"
178
- end
179
-
180
- def get_properties_string(properties)
181
- p = properties.map do |key, value|
182
- "#{key}: #{value.inspect}"
183
- end.join(', ')
184
- p.size == 0 ? '' : " {#{p}}"
185
- end
186
-
187
164
  def origin_association
188
165
  target_class.associations[@origin]
189
166
  end
@@ -0,0 +1,108 @@
1
+ module Neo4j
2
+ module ActiveNode
3
+ module HasN
4
+ module AssociationCypherMethods
5
+ # Return cypher partial query string for the relationship part of a MATCH (arrow / relationship definition)
6
+ def arrow_cypher(var = nil, properties = {}, create = false, reverse = false, length = nil)
7
+ validate_origin!
8
+
9
+ if create && length.present?
10
+ fail(ArgumentError, 'rel_length option cannot be specified when creating a relationship')
11
+ end
12
+
13
+ direction_cypher(get_relationship_cypher(var, properties, create, length), create, reverse)
14
+ end
15
+
16
+ private
17
+
18
+ def direction_cypher(relationship_cypher, create, reverse = false)
19
+ case get_direction(create, reverse)
20
+ when :out
21
+ "-#{relationship_cypher}->"
22
+ when :in
23
+ "<-#{relationship_cypher}-"
24
+ when :both
25
+ "-#{relationship_cypher}-"
26
+ end
27
+ end
28
+
29
+ def get_relationship_cypher(var, properties, create, length)
30
+ relationship_type = relationship_type(create)
31
+ relationship_name_cypher = ":`#{relationship_type}`" if relationship_type
32
+ rel_length_cypher = cypher_for_rel_length(length)
33
+ properties_string = get_properties_string(properties)
34
+
35
+ "[#{var}#{relationship_name_cypher}#{rel_length_cypher}#{properties_string}]"
36
+ end
37
+
38
+ def get_properties_string(properties)
39
+ p = properties.map do |key, value|
40
+ "#{key}: #{value.inspect}"
41
+ end.join(', ')
42
+ p.size == 0 ? '' : " {#{p}}"
43
+ end
44
+
45
+ VALID_REL_LENGTH_SYMBOLS = {
46
+ any: '*'
47
+ }
48
+
49
+ def cypher_for_rel_length(length)
50
+ return nil if length.blank?
51
+
52
+ validate_rel_length!(length)
53
+
54
+ case length
55
+ when Symbol then VALID_REL_LENGTH_SYMBOLS[length]
56
+ when Fixnum then "*#{length}"
57
+ when Range then cypher_for_range_rel_length(length)
58
+ when Hash then cypher_for_hash_rel_length(length)
59
+ end
60
+ end
61
+
62
+ def cypher_for_range_rel_length(length)
63
+ range_end = length.end
64
+ range_end = nil if range_end == Float::INFINITY
65
+ "*#{length.begin}..#{range_end}"
66
+ end
67
+
68
+ def cypher_for_hash_rel_length(length)
69
+ range_end = length[:max]
70
+ range_end = nil if range_end == Float::INFINITY
71
+ "*#{length[:min]}..#{range_end}"
72
+ end
73
+
74
+ def validate_rel_length!(length)
75
+ message = rel_length_error_message(length)
76
+ fail(ArgumentError, "Invalid value for rel_length (#{length.inspect}): #{message}") if message
77
+ true
78
+ end
79
+
80
+ def rel_length_error_message(length)
81
+ case length
82
+ when Fixnum then 'cannot be negative' if length < 0
83
+ when Symbol then rel_length_symbol_error_message(length)
84
+ when Range then rel_length_range_error_message(length)
85
+ when Hash then rel_length_hash_error_message(length)
86
+ else 'should be a Symbol, Fixnum, Range or Hash'
87
+ end
88
+ end
89
+
90
+ def rel_length_symbol_error_message(length)
91
+ "expecting one of #{VALID_REL_LENGTH_SYMBOLS.keys.inspect}" if !VALID_REL_LENGTH_SYMBOLS.key?(length)
92
+ end
93
+
94
+ def rel_length_range_error_message(length)
95
+ if length.begin > length.end
96
+ 'cannot be a decreasing Range'
97
+ elsif length.begin < 0
98
+ 'cannot include negative values'
99
+ end
100
+ end
101
+
102
+ def rel_length_hash_error_message(length)
103
+ 'Hash keys should be a subset of [:min, :max]' if (length.keys & [:min, :max]) != length.keys
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -34,7 +34,7 @@ module Neo4j::ActiveNode
34
34
  #
35
35
  module IdProperty
36
36
  extend ActiveSupport::Concern
37
-
37
+ include Accessor
38
38
 
39
39
  module TypeMethods
40
40
  def define_id_methods(clazz, name, conf)
@@ -90,7 +90,7 @@ module Neo4j::ActiveNode
90
90
  end
91
91
 
92
92
  def #{name}
93
- default_property :#{name}
93
+ default_property_value
94
94
  end
95
95
 
96
96
  alias_method :id, :#{name}
@@ -102,12 +102,12 @@ module Neo4j::ActiveNode
102
102
 
103
103
  clazz.module_eval(%{
104
104
  default_property :#{name} do |instance|
105
- raise "Specifying custom id_property #{name} on none existing method #{on}" unless instance.respond_to?(:#{on})
106
- instance.#{on}
105
+ raise "Specifying custom id_property #{name} on non-existent method #{on}" unless instance.respond_to?(:#{on})
106
+ instance.#{on}
107
107
  end
108
108
 
109
109
  def #{name}
110
- default_property :#{name}
110
+ default_property_value
111
111
  end
112
112
 
113
113
  alias_method :id, :#{name}
@@ -124,6 +124,8 @@ module Neo4j::ActiveNode
124
124
 
125
125
 
126
126
  module ClassMethods
127
+ attr_accessor :manual_id_property
128
+
127
129
  def find_by_neo_id(id)
128
130
  Neo4j::Node.load(id)
129
131
  end
@@ -137,12 +139,14 @@ module Neo4j::ActiveNode
137
139
  end
138
140
 
139
141
  def id_property(name, conf = {})
140
- id_property_constraint(name)
141
- @id_property_info = {name: name, type: conf}
142
- TypeMethods.define_id_methods(self, name, conf)
143
- constraint name, type: :unique unless conf[:constraint] == false
142
+ self.manual_id_property = true
143
+ Neo4j::Session.on_session_available do |_|
144
+ @id_property_info = {name: name, type: conf}
145
+ TypeMethods.define_id_methods(self, name, conf)
146
+ constraint(name, type: :unique) unless conf[:constraint] == false
144
147
 
145
- self.define_singleton_method(:find_by_id) { |key| self.where(name => key).first }
148
+ self.define_singleton_method(:find_by_id) { |key| self.where(name => key).first }
149
+ end
146
150
  end
147
151
 
148
152
  # rubocop:disable Style/PredicateName
@@ -165,6 +169,10 @@ module Neo4j::ActiveNode
165
169
  id_property_info[:name]
166
170
  end
167
171
 
172
+ def manual_id_property?
173
+ !!manual_id_property
174
+ end
175
+
168
176
  alias_method :primary_key, :id_property_name
169
177
 
170
178
  private
@@ -173,7 +181,7 @@ module Neo4j::ActiveNode
173
181
  if id_property?
174
182
  unless mapped_label.uniqueness_constraints[:property_keys].include?([name])
175
183
  # Neo4j Embedded throws a crazy error when a constraint can't be dropped
176
- drop_constraint(id_property_name, type: :unique)
184
+ drop_constraint(id_property_name, type: :unique) if constraint?(mapped_label_name, id_property_name)
177
185
  end
178
186
  end
179
187
  rescue Neo4j::Server::CypherResponse::ResponseError, Java::OrgNeo4jCypher::CypherExecutionException
@@ -0,0 +1,62 @@
1
+ module Neo4j::ActiveNode::IdProperty
2
+ # Provides get/set of the Id Property values.
3
+ # Some methods
4
+ module Accessor
5
+ extend ActiveSupport::Concern
6
+
7
+ attr_reader :default_property_value
8
+
9
+ def default_properties=(properties)
10
+ @default_property_value = properties[default_property_key]
11
+ end
12
+
13
+ def default_property(key)
14
+ return nil unless key == default_property_key
15
+ default_property_value
16
+ end
17
+
18
+ def default_property_key
19
+ self.class.default_property_key
20
+ end
21
+
22
+ def default_properties
23
+ @default_properties ||= Hash.new(nil)
24
+ end
25
+
26
+ module ClassMethods
27
+ def default_property_key
28
+ @default_property_key ||= default_properties_keys.first
29
+ end
30
+
31
+ # TODO: Move this to the DeclaredPropertyManager
32
+ def default_property(name, &block)
33
+ reset_default_properties(name) if default_properties.respond_to?(:size)
34
+ default_properties[name] = block
35
+ end
36
+
37
+ # @return [Hash<Symbol,Proc>]
38
+ def default_properties
39
+ @default_property ||= {}
40
+ end
41
+
42
+ def default_properties_keys
43
+ @default_properties_keys ||= default_properties.keys
44
+ end
45
+
46
+ def reset_default_properties(name_to_keep)
47
+ default_properties.each_key do |property|
48
+ @default_properties_keys = nil
49
+ undef_method(property) unless property == name_to_keep
50
+ end
51
+ @default_properties_keys = nil
52
+ @default_property = {}
53
+ end
54
+
55
+ def default_property_values(instance)
56
+ default_properties.each_with_object({}) do |(key, block), result|
57
+ result[key] = block.call(instance)
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -103,7 +103,7 @@ module Neo4j
103
103
  # Finds the first record matching the specified conditions. There is no implied ordering so if order matters, you should specify it yourself.
104
104
  # @param Hash args of arguments to find
105
105
  def find_by(values)
106
- all.query_as(:n).where(n: values).limit(1).pluck(:n).first
106
+ all.where(values).limit(1).query_as(:n).pluck(:n).first
107
107
  end
108
108
 
109
109
  # Like find_by, except that if no record is found, raises a RecordNotFound error.
@@ -147,6 +147,7 @@ module Neo4j
147
147
  # end
148
148
  def index(property, conf = {})
149
149
  Neo4j::Session.on_session_available do |_|
150
+ drop_constraint(property, type: :unique) if Neo4j::Label.constraint?(mapped_label_name, property)
150
151
  _index(property, conf)
151
152
  end
152
153
  indexed_properties.push property unless indexed_properties.include? property
@@ -161,12 +162,22 @@ module Neo4j
161
162
  Neo4j::Session.on_session_available do |session|
162
163
  unless Neo4j::Label.constraint?(mapped_label_name, property)
163
164
  label = Neo4j::Label.create(mapped_label_name)
165
+ drop_index(property, label) if index?(property)
164
166
  label.create_constraint(property, constraints, session)
165
167
  end
166
168
  end
167
169
  end
168
170
 
169
- def drop_constraint(property, constraint)
171
+ # @param [Symbol] property The name of the property index to be dropped
172
+ # @param [Neo4j::Label] label An instance of label from Neo4j::Core
173
+ def drop_index(property, label = nil)
174
+ label_obj = label || Neo4j::Label.create(mapped_label_name)
175
+ label_obj.drop_index(property)
176
+ end
177
+
178
+ # @param [Symbol] property The name of the property constraint to be dropped
179
+ # @param [Hash] constraint The constraint type to be dropped.
180
+ def drop_constraint(property, constraint = {type: :unique})
170
181
  Neo4j::Session.on_session_available do |session|
171
182
  label = Neo4j::Label.create(mapped_label_name)
172
183
  label.drop_constraint(property, constraint, session)
@@ -24,8 +24,10 @@ module Neo4j::ActiveNode
24
24
  # If any of the before_* callbacks return false the action is cancelled and save returns false.
25
25
  def save(*)
26
26
  update_magic_properties
27
- association_proxy_cache.clear
28
- create_or_update
27
+ cascade_save do
28
+ association_proxy_cache.clear
29
+ create_or_update
30
+ end
29
31
  end
30
32
 
31
33
  # Persist the object to the database. Validations and Callbacks are included
@@ -53,7 +55,7 @@ module Neo4j::ActiveNode
53
55
  node = _create_node(properties)
54
56
  init_on_load(node, node.props)
55
57
  send_props(@relationship_props) if @relationship_props
56
- @relationship_props = nil
58
+ @relationship_props = @deferred_nodes = nil
57
59
  true
58
60
  end
59
61
 
@@ -66,12 +68,25 @@ module Neo4j::ActiveNode
66
68
  session.create_node(props, labels)
67
69
  end
68
70
 
71
+ private
72
+
73
+ # The pending associations are cleared during the save process, so it's necessary to
74
+ # build the processable hash before it begins. If there are nodes and associations that
75
+ # need to be created after the node is saved, a new transaction is started.
76
+ def cascade_save
77
+ deferred_nodes = pending_associations_with_nodes
78
+ Neo4j::Transaction.run(!deferred_nodes.blank?) do
79
+ result = yield
80
+ process_unpersisted_nodes!(deferred_nodes) if deferred_nodes
81
+ result
82
+ end
83
+ end
84
+
69
85
  module ClassMethods
70
86
  # Creates and saves a new node
71
87
  # @param [Hash] props the properties the new node should have
72
88
  def create(props = {})
73
89
  association_props = extract_association_attributes!(props) || {}
74
-
75
90
  new(props).tap do |obj|
76
91
  yield obj if block_given?
77
92
  obj.save
@@ -3,9 +3,9 @@ module Neo4j::ActiveNode
3
3
  extend ActiveSupport::Concern
4
4
  include Neo4j::Shared::Property
5
5
 
6
- def initialize(attributes = {}, options = {})
7
- super(attributes, options)
8
- @attributes ||= self.class.attributes_nil_hash.dup
6
+ def initialize(attributes = nil)
7
+ super(attributes)
8
+ @attributes ||= Hash[self.class.attributes_nil_hash]
9
9
  send_props(@relationship_props) if _persisted_obj && !@relationship_props.nil?
10
10
  end
11
11
 
@@ -22,6 +22,7 @@ module Neo4j::ActiveNode
22
22
  private
23
23
 
24
24
  def contains_association?(attributes)
25
+ return false unless attributes
25
26
  attributes.each_key { |key| return true if associations_keys.include?(key) }
26
27
  false
27
28
  end
@@ -6,6 +6,7 @@ module Neo4j
6
6
  include Neo4j::ActiveNode::Query::QueryProxyMethods
7
7
  include Neo4j::ActiveNode::Query::QueryProxyFindInBatches
8
8
  include Neo4j::ActiveNode::Query::QueryProxyEagerLoading
9
+ include Neo4j::ActiveNode::Query::QueryProxyUnpersisted
9
10
  include Neo4j::ActiveNode::Dependent::QueryProxyMethods
10
11
 
11
12
  # The most recent node to start a QueryProxy chain.
@@ -26,11 +27,13 @@ module Neo4j
26
27
  # @param [Neo4j::ActiveNode::HasN::Association] association The ActiveNode association (an object created by a <tt>has_one</tt> or
27
28
  # <tt>has_many</tt>) that created this object.
28
29
  # @param [Hash] options Additional options pertaining to the QueryProxy object. These may include:
29
- # * node_var: A string or symbol to be used by Cypher within its query string as an identifier
30
- # * rel_var: Same as above but pertaining to a relationship identifier
31
- # * session: The session to be used for this query
32
- # * source_object: The node instance at the start of the QueryProxy chain
33
- # * query_proxy: An existing QueryProxy chain upon which this new object should be built
30
+ # @option options [String, Symbol] :node_var A string or symbol to be used by Cypher within its query string as an identifier
31
+ # @option options [String, Symbol] :rel_var Same as above but pertaining to a relationship identifier
32
+ # @option options [Range, Fixnum, Symbol, Hash] :rel_length A Range, a Fixnum, a Hash or a Symbol to indicate the variable-length/fixed-length
33
+ # qualifier of the relationship. See http://neo4jrb.readthedocs.org/en/latest/Querying.html#variable-length-relationships.
34
+ # @option options [Neo4j::Session] :session The session to be used for this query
35
+ # @option options [Neo4j::ActiveNode] :source_object The node instance at the start of the QueryProxy chain
36
+ # @option options [QueryProxy] :query_proxy An existing QueryProxy chain upon which this new object should be built
34
37
  #
35
38
  # QueryProxy objects are evaluated lazily.
36
39
  def initialize(model, association = nil, options = {})
@@ -51,7 +54,7 @@ module Neo4j
51
54
  end
52
55
 
53
56
  def inspect
54
- "<QueryProxy #{ANSI::CYAN}#{@context}#{ANSI::CLEAR} CYPHER: #{ANSI::YELLOW}#{self.to_cypher.inspect}#{ANSI::CLEAR}>"
57
+ "#<QueryProxy #{@context} CYPHER: #{self.to_cypher.inspect}>"
55
58
  end
56
59
 
57
60
  attr_reader :start_object, :query_proxy
@@ -84,6 +87,9 @@ module Neo4j
84
87
  # Build a Neo4j::Core::Query object for the QueryProxy. This is necessary when you want to take an existing QueryProxy chain
85
88
  # and work with it from the more powerful (but less friendly) Neo4j::Core::Query.
86
89
  # @param [String,Symbol] var The identifier to use for node at this link of the QueryProxy chain.
90
+ #
91
+ # .. code-block:: ruby
92
+ #
87
93
  # student.lessons.query_as(:l).with('your cypher here...')
88
94
  def query_as(var, with_labels = true)
89
95
  result_query = @chain.inject(base_query(var, with_labels).params(@params)) do |query, link|
@@ -115,6 +121,8 @@ module Neo4j
115
121
 
116
122
  # Scope all queries to the current scope.
117
123
  #
124
+ # .. code-block:: ruby
125
+ #
118
126
  # Comment.where(post_id: 1).scoping do
119
127
  # Comment.first
120
128
  # end
@@ -141,8 +149,12 @@ module Neo4j
141
149
  alias_method :order_by, :order
142
150
 
143
151
  # Cypher string for the QueryProxy's query. This will not include params. For the full output, see <tt>to_cypher_with_params</tt>.
144
- def to_cypher
145
- query.to_cypher
152
+ def to_cypher(*args)
153
+ query.to_cypher(*args)
154
+ end
155
+
156
+ def print_cypher
157
+ query.print_cypher
146
158
  end
147
159
 
148
160
  # Returns a string of the cypher query with return objects and params
@@ -155,8 +167,7 @@ module Neo4j
155
167
 
156
168
  # To add a relationship for the node for the association on this QueryProxy
157
169
  def <<(other_node)
158
- create(other_node, {})
159
-
170
+ @start_object._persisted_obj ? create(other_node, {}) : defer_create(other_node, {}, :<<)
160
171
  self
161
172
  end
162
173
 
@@ -278,7 +289,7 @@ module Neo4j
278
289
  end
279
290
 
280
291
  def _association_arrow(properties = {}, create = false)
281
- @association && @association.arrow_cypher(@rel_var, properties, create)
292
+ @association && @association.arrow_cypher(@rel_var, properties, create, false, @rel_length)
282
293
  end
283
294
 
284
295
  def _chain_level
@@ -312,8 +323,13 @@ module Neo4j
312
323
  private
313
324
 
314
325
  def instance_vars_from_options!(options)
315
- @node_var, @session, @source_object, @starting_query, @optional, @start_object, @query_proxy, @chain_level, @association_labels =
316
- options.values_at(:node, :session, :source_object, :starting_query, :optional, :start_object, :query_proxy, :chain_level, :association_labels)
326
+ @node_var, @session, @source_object, @starting_query, @optional,
327
+ @start_object, @query_proxy, @chain_level, @association_labels,
328
+ @rel_length = options.values_at(:node, :session, :source_object,
329
+ :starting_query, :optional,
330
+ :start_object, :query_proxy,
331
+ :chain_level, :association_labels,
332
+ :rel_length)
317
333
  end
318
334
 
319
335
  def build_deeper_query_proxy(method, args)