neo4j 4.0.0 → 4.1.0

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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +10 -0
  3. data/Gemfile +6 -16
  4. data/README.md +5 -2
  5. data/bin/neo4j-jars +6 -6
  6. data/lib/neo4j.rb +13 -9
  7. data/lib/neo4j/active_node.rb +9 -9
  8. data/lib/neo4j/active_node/dependent.rb +11 -0
  9. data/lib/neo4j/active_node/dependent/association_methods.rb +28 -0
  10. data/lib/neo4j/active_node/dependent/query_proxy_methods.rb +48 -0
  11. data/lib/neo4j/active_node/has_n.rb +124 -112
  12. data/lib/neo4j/active_node/has_n/association.rb +45 -30
  13. data/lib/neo4j/active_node/id_property.rb +22 -19
  14. data/lib/neo4j/active_node/initialize.rb +2 -4
  15. data/lib/neo4j/active_node/labels.rb +23 -22
  16. data/lib/neo4j/active_node/node_wrapper.rb +5 -8
  17. data/lib/neo4j/active_node/orm_adapter.rb +2 -4
  18. data/lib/neo4j/active_node/persistence.rb +5 -10
  19. data/lib/neo4j/active_node/property.rb +3 -4
  20. data/lib/neo4j/active_node/query.rb +27 -6
  21. data/lib/neo4j/active_node/query/query_proxy.rb +65 -110
  22. data/lib/neo4j/active_node/query/query_proxy_enumerable.rb +67 -0
  23. data/lib/neo4j/active_node/query/query_proxy_find_in_batches.rb +0 -1
  24. data/lib/neo4j/active_node/query/query_proxy_methods.rb +29 -28
  25. data/lib/neo4j/active_node/query_methods.rb +6 -6
  26. data/lib/neo4j/active_node/reflection.rb +3 -2
  27. data/lib/neo4j/active_node/rels.rb +1 -1
  28. data/lib/neo4j/active_node/scope.rb +13 -8
  29. data/lib/neo4j/active_node/validations.rb +5 -6
  30. data/lib/neo4j/active_rel.rb +1 -2
  31. data/lib/neo4j/active_rel/callbacks.rb +3 -3
  32. data/lib/neo4j/active_rel/persistence.rb +9 -7
  33. data/lib/neo4j/active_rel/property.rb +12 -4
  34. data/lib/neo4j/active_rel/query.rb +6 -8
  35. data/lib/neo4j/active_rel/rel_wrapper.rb +0 -2
  36. data/lib/neo4j/active_rel/related_node.rb +4 -5
  37. data/lib/neo4j/active_rel/types.rb +4 -6
  38. data/lib/neo4j/active_rel/validations.rb +0 -1
  39. data/lib/neo4j/config.rb +11 -23
  40. data/lib/neo4j/core/query.rb +1 -1
  41. data/lib/neo4j/migration.rb +17 -18
  42. data/lib/neo4j/paginated.rb +4 -4
  43. data/lib/neo4j/railtie.rb +19 -19
  44. data/lib/neo4j/shared.rb +7 -3
  45. data/lib/neo4j/shared/callbacks.rb +15 -4
  46. data/lib/neo4j/shared/identity.rb +2 -2
  47. data/lib/neo4j/shared/persistence.rb +10 -21
  48. data/lib/neo4j/shared/property.rb +17 -30
  49. data/lib/neo4j/shared/rel_type_converters.rb +1 -3
  50. data/lib/neo4j/shared/type_converters.rb +13 -25
  51. data/lib/neo4j/shared/validations.rb +3 -3
  52. data/lib/neo4j/tasks/migration.rake +7 -7
  53. data/lib/neo4j/type_converters.rb +1 -1
  54. data/lib/neo4j/version.rb +1 -1
  55. data/lib/rails/generators/neo4j/model/model_generator.rb +16 -12
  56. data/lib/rails/generators/neo4j_generator.rb +18 -18
  57. data/neo4j.gemspec +22 -18
  58. metadata +103 -1
@@ -22,7 +22,7 @@ module Neo4j
22
22
  # Get an instance by id of the model
23
23
  def get!(id)
24
24
  klass.find(wrap_key(id)).tap do |node|
25
- raise "No record found" if node.nil?
25
+ fail 'No record found' if node.nil?
26
26
  end
27
27
  end
28
28
 
@@ -68,7 +68,7 @@ module Neo4j
68
68
  private
69
69
 
70
70
  def hasherize_order(order)
71
- (order || []).map {|clause| Hash[*clause] }
71
+ (order || []).map { |clause| Hash[*clause] }
72
72
  end
73
73
 
74
74
  def extract_id!(conditions)
@@ -76,8 +76,6 @@ module Neo4j
76
76
  conditions[klass.id_property_name.to_sym] = id
77
77
  end
78
78
  end
79
-
80
79
  end
81
80
  end
82
81
  end
83
-
@@ -1,12 +1,11 @@
1
1
  module Neo4j::ActiveNode
2
2
  module Persistence
3
-
4
3
  class RecordInvalidError < RuntimeError
5
4
  attr_reader :record
6
5
 
7
6
  def initialize(record)
8
7
  @record = record
9
- super(@record.errors.full_messages.join(", "))
8
+ super(@record.errors.full_messages.join(', '))
10
9
  end
11
10
  end
12
11
 
@@ -18,7 +17,7 @@ module Neo4j::ActiveNode
18
17
  # If the model is new a record gets created in the database, otherwise the existing record gets updated.
19
18
  # If perform_validation is true validations run.
20
19
  # 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.
21
- # Theres a series of callbacks associated with save. If any of the before_* callbacks return false the action is cancelled and save returns false.
20
+ # 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.
22
21
  def save(*)
23
22
  update_magic_properties
24
23
  clear_association_cache
@@ -36,9 +35,7 @@ module Neo4j::ActiveNode
36
35
  # @see Neo4j::Rails::Validations Neo4j::Rails::Validations - for the :validate parameter
37
36
  # @see Neo4j::Rails::Callbacks Neo4j::Rails::Callbacks - for callbacks
38
37
  def save!(*args)
39
- unless save(*args)
40
- raise RecordInvalidError.new(self)
41
- end
38
+ fail RecordInvalidError, self unless save(*args)
42
39
  end
43
40
 
44
41
  # Creates a model with values matching those of the instance attributes and returns its id.
@@ -70,7 +67,7 @@ module Neo4j::ActiveNode
70
67
  module ClassMethods
71
68
  # Creates and saves a new node
72
69
  # @param [Hash] props the properties the new node should have
73
- def create(props = {}, &block)
70
+ def create(props = {})
74
71
  association_props = extract_association_attributes!(props)
75
72
 
76
73
  new(props).tap do |obj|
@@ -83,7 +80,7 @@ module Neo4j::ActiveNode
83
80
  end
84
81
 
85
82
  # Same as #create, but raises an error if there is a problem during save.
86
- def create!(*args, &block)
83
+ def create!(*args)
87
84
  props = args[0] || {}
88
85
  association_props = extract_association_attributes!(props)
89
86
 
@@ -109,10 +106,8 @@ module Neo4j::ActiveNode
109
106
  def load_entity(id)
110
107
  Neo4j::Node.load(id)
111
108
  end
112
-
113
109
  end
114
110
 
115
111
  private
116
-
117
112
  end
118
113
  end
@@ -3,19 +3,18 @@ module Neo4j::ActiveNode
3
3
  extend ActiveSupport::Concern
4
4
  include Neo4j::Shared::Property
5
5
 
6
- def initialize(attributes={}, options={})
6
+ def initialize(attributes = {}, options = {})
7
7
  super(attributes, options)
8
8
 
9
- send_props(@relationship_props) if persisted? and not @relationship_props.nil?
9
+ send_props(@relationship_props) if persisted? && !@relationship_props.nil?
10
10
  end
11
11
 
12
12
  module ClassMethods
13
-
14
13
  # Extracts keys from attributes hash which are relationships of the model
15
14
  # TODO: Validate separately that relationships are getting the right values? Perhaps also store the values and persist relationships on save?
16
15
  def extract_association_attributes!(attributes)
17
16
  attributes.keys.each_with_object({}) do |key, association_props|
18
- association_props[key] = attributes.delete(key) if self.has_association?(key)
17
+ association_props[key] = attributes.delete(key) if self.association?(key)
19
18
  end
20
19
  end
21
20
  end
@@ -1,6 +1,5 @@
1
1
  module Neo4j
2
2
  module ActiveNode
3
-
4
3
  # Helper methods to return Neo4j::Core::Query objects. A query object can be used to successively build a cypher query
5
4
  #
6
5
  # person.query_as(:n).match('n-[:friend]-o').return(o: :name) # Return the names of all the person's friends
@@ -12,12 +11,25 @@ module Neo4j
12
11
  #
13
12
  # @example Return the names of all of Mike's friends
14
13
  # # Generates: MATCH (mike:Person), mike-[:friend]-friend WHERE ID(mike) = 123 RETURN friend.name
15
- # mike.query_as(:mike).match('mike-[:friend]-friend').return(friend: :name)
14
+ # mike.query_as(:mike).match('mike-[:friend]-friend').return(friend: :name)
16
15
  #
17
16
  # @param var [Symbol, String] The variable name to specify in the query
18
17
  # @return [Neo4j::Core::Query]
19
- def query_as(var)
20
- self.class.query_as(var).where("ID(#{var})" => self.neo_id)
18
+ def query_as(node_var)
19
+ self.class.query_as(node_var).where("ID(#{node_var})" => self.neo_id)
20
+ end
21
+
22
+ # Starts a new QueryProxy with the starting identifier set to the given argument and QueryProxy caller set to the node instance.
23
+ # This method does not exist within QueryProxy and can only be used to start a new chain.
24
+ #
25
+ # @example Start a new QueryProxy chain with the first identifier set manually
26
+ # # Generates: MATCH (s:`Student`), (l:`Lesson`), s-[rel1:`ENROLLED_IN`]->(l:`Lesson`) WHERE ID(s) = {neo_id_17963}
27
+ # student.as(:s).lessons(:l)
28
+ #
29
+ # @param [String, Symbol] node_var The identifier to use within the QueryProxy object
30
+ # @return [Neo4j::ActiveNode::Query::QueryProxy]
31
+ def as(node_var)
32
+ self.class.query_proxy(node: node_var, caller: self).match_to(self)
21
33
  end
22
34
 
23
35
  module ClassMethods
@@ -34,7 +46,7 @@ module Neo4j
34
46
  end
35
47
 
36
48
  Neo4j::ActiveNode::Query::QueryProxy::METHODS.each do |method|
37
- module_eval(%Q{
49
+ module_eval(%{
38
50
  def #{method}(*args)
39
51
  self.query_proxy.#{method}(*args)
40
52
  end}, __FILE__, __LINE__)
@@ -44,10 +56,19 @@ module Neo4j
44
56
  Neo4j::ActiveNode::Query::QueryProxy.new(self, nil, options)
45
57
  end
46
58
 
59
+ # Start a new QueryProxy with the starting identifier set to the given argument.
60
+ # This method does not exist within QueryProxy, it can only be called at the class level to create a new QP object.
61
+ # To set an identifier within a QueryProxy chain, give it as the first argument to a chained association.
62
+ #
63
+ # @example Start a new QueryProxy where the first identifier is set manually.
64
+ # # Generates: MATCH (s:`Student`), (result_lessons:`Lesson`), s-[rel1:`ENROLLED_IN`]->(result_lessons:`Lesson`)
65
+ # Student.as(:s).lessons
66
+ #
67
+ # @param [String, Symbol] node_var A string or symbol to use as the starting identifier.
68
+ # @return [Neo4j::ActiveNode::Query::QueryProxy]
47
69
  def as(node_var)
48
70
  query_proxy(node: node_var)
49
71
  end
50
-
51
72
  end
52
73
  end
53
74
  end
@@ -2,10 +2,10 @@ module Neo4j
2
2
  module ActiveNode
3
3
  module Query
4
4
  class QueryProxy
5
-
6
- include Enumerable
5
+ include Neo4j::ActiveNode::Query::QueryProxyEnumerable
7
6
  include Neo4j::ActiveNode::Query::QueryProxyMethods
8
7
  include Neo4j::ActiveNode::Query::QueryProxyFindInBatches
8
+ include Neo4j::ActiveNode::Dependent::QueryProxyMethods
9
9
 
10
10
  # The most recent node to start a QueryProxy chain.
11
11
  # Will be nil when using QueryProxy chains on class methods.
@@ -49,82 +49,18 @@ module Neo4j
49
49
 
50
50
  # 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
51
51
  # in the QueryProxy chain.
52
+ attr_reader :node_var
52
53
  def identity
53
54
  @node_var || _result_string
54
55
  end
55
56
  alias_method :node_identity, :identity
56
57
 
57
58
  # The relationship identifier most recently used by the QueryProxy chain.
59
+ attr_reader :rel_var
58
60
  def rel_identity
59
- @rel_var
60
- end
61
-
62
- # Executes the query against the database if the results are not already present in a node's association cache. This method is
63
- # shared by <tt>each</tt>, <tt>each_rel</tt>, and <tt>each_with_rel</tt>.
64
- # @param [String,Symbol] node The string or symbol of the node to return from the database.
65
- # @param [String,Symbol] rel The string or symbol of a relationship to return from the database.
66
- def enumerable_query(node, rel = nil)
67
- pluck_this = rel.nil? ? [node] : [node, rel]
68
- return self.pluck(*pluck_this) if @association.nil? || caller.nil?
69
- cypher_string = self.to_cypher_with_params(pluck_this)
70
- association_collection = caller.association_instance_get(cypher_string, @association)
71
- if association_collection.nil?
72
- association_collection = self.pluck(*pluck_this)
73
- caller.association_instance_set(cypher_string, association_collection, @association) unless association_collection.empty?
74
- end
75
- association_collection
76
- end
77
-
78
- # Just like every other <tt>each</tt> but it allows for optional params to support the versions that also return relationships.
79
- # The <tt>node</tt> and <tt>rel</tt> params are typically used by those other methods but there's nothing stopping you from
80
- # using `your_node.each(true, true)` instead of `your_node.each_with_rel`.
81
- # @return [Enumerable] An enumerable containing some combination of nodes and rels.
82
- def each(node = true, rel = nil, &block)
83
- if node && rel
84
- enumerable_query(identity, @rel_var).each { |obj, rel| yield obj, rel }
85
- else
86
- pluck_this = !rel ? identity : @rel_var
87
- enumerable_query(pluck_this).each { |obj| yield obj }
88
- end
89
- end
90
-
91
- # When called at the end of a QueryProxy chain, it will return the resultant relationship objects intead of nodes.
92
- # For example, to return the relationship between a given student and their lessons:
93
- # student.lessons.each_rel do |rel|
94
- # @return [Enumerable] An enumerable containing any number of applicable relationship objects.
95
- def each_rel(&block)
96
- block_given? ? each(false, true, &block) : to_enum(:each, false, true)
97
- end
61
+ ActiveSupport::Deprecation.warn 'rel_identity is deprecated and may be removed from future releases, use rel_var instead.', caller
98
62
 
99
- # When called at the end of a QueryProxy chain, it will return the nodes and relationships of the last link.
100
- # For example, to return a lesson and each relationship to a given student:
101
- # student.lessons.each_with_rel do |lesson, rel|
102
- def each_with_rel(&block)
103
- block_given? ? each(true, true, &block) : to_enum(:each, true, true)
104
- end
105
-
106
- # Does exactly what you would hope. Without it, comparing `bobby.lessons == sandy.lessons` would evaluate to false because it
107
- # would be comparing the QueryProxy objects, not the lessons themselves.
108
- def ==(value)
109
- self.to_a == value
110
- end
111
-
112
- METHODS = %w[where rel_where order skip limit]
113
-
114
- METHODS.each do |method|
115
- module_eval(%Q{
116
- def #{method}(*args)
117
- build_deeper_query_proxy(:#{method}, args)
118
- end}, __FILE__, __LINE__)
119
- end
120
- # Since there is a rel_where method, it seems only natural for there to be node_where
121
- alias_method :node_where, :where
122
- alias_method :offset, :skip
123
- alias_method :order_by, :order
124
-
125
- # For getting variables which have been defined as part of the association chain
126
- def pluck(*args)
127
- self.query.pluck(*args)
63
+ @rel_var
128
64
  end
129
65
 
130
66
  def params(params)
@@ -143,15 +79,15 @@ module Neo4j
143
79
  # @param [String,Symbol] var The identifier to use for node at this link of the QueryProxy chain.
144
80
  # student.lessons.query_as(:l).with('your cypher here...')
145
81
  def query_as(var)
146
- query = if @association
147
- chain_var = _association_chain_var
148
- label_string = @model && ":`#{@model.mapped_label_name}`"
149
- (_association_query_start(chain_var) & _query_model_as(var)).send(_match_type, "#{chain_var}#{_association_arrow}(#{var}#{label_string})")
150
- else
151
- starting_query ? (starting_query & _query_model_as(var)) : _query_model_as(var)
152
- end
82
+ base_query = if @association
83
+ chain_var = _association_chain_var
84
+ label_string = @model && ":`#{@model.mapped_label_name}`"
85
+ (_association_query_start(chain_var) & _query_model_as(var)).send(_match_type, "#{chain_var}#{_association_arrow}(#{var}#{label_string})")
86
+ else
87
+ starting_query ? (starting_query & _query_model_as(var)) : _query_model_as(var)
88
+ end
153
89
  # Build a query chain via the chain, return the result
154
- @chain.inject(query.params(@params)) do |query, (method, arg)|
90
+ @chain.inject(base_query.params(@params)) do |query, (method, arg)|
155
91
  query.send(method, arg.respond_to?(:call) ? arg.call(var) : arg)
156
92
  end
157
93
  end
@@ -172,7 +108,18 @@ module Neo4j
172
108
  @model.current_scope = previous
173
109
  end
174
110
 
111
+ METHODS = %w(where rel_where order skip limit)
175
112
 
113
+ METHODS.each do |method|
114
+ module_eval(%{
115
+ def #{method}(*args)
116
+ build_deeper_query_proxy(:#{method}, args)
117
+ end}, __FILE__, __LINE__)
118
+ end
119
+ # Since there is a rel_where method, it seems only natural for there to be node_where
120
+ alias_method :node_where, :where
121
+ alias_method :offset, :skip
122
+ alias_method :order_by, :order
176
123
 
177
124
  # Cypher string for the QueryProxy's query. This will not include params. For the full output, see <tt>to_cypher_with_params</tt>.
178
125
  def to_cypher
@@ -201,7 +148,7 @@ module Neo4j
201
148
  end
202
149
 
203
150
  def create(other_nodes, properties)
204
- raise "Can only create associations on associations" unless @association
151
+ fail 'Can only create associations on associations' unless @association
205
152
  other_nodes = [other_nodes].flatten
206
153
  properties = @association.inject_classname(properties)
207
154
  other_nodes = other_nodes.map do |other_node|
@@ -213,34 +160,34 @@ module Neo4j
213
160
  end
214
161
  end.compact
215
162
 
216
- raise ArgumentError, "Node must be of the association's class when model is specified" if @model && other_nodes.any? {|other_node| !other_node.is_a?(@model) }
163
+ fail ArgumentError, "Node must be of the association's class when model is specified" if @model && other_nodes.any? { |other_node| !other_node.is_a?(@model) }
217
164
  other_nodes.each do |other_node|
218
- #Neo4j::Transaction.run do
219
- other_node.save if not other_node.persisted?
165
+ # Neo4j::Transaction.run do
166
+ other_node.save unless other_node.neo_id
220
167
 
221
- return false if @association.perform_callback(@options[:start_object], other_node, :before) == false
168
+ return false if @association.perform_callback(@options[:start_object], other_node, :before) == false
222
169
 
223
- start_object = @options[:start_object]
224
- start_object.clear_association_cache
225
- _session.query(context: @options[:context])
226
- .match("(start#{match_string(start_object)}), (end#{match_string(other_node)})").where("ID(start) = {start_id} AND ID(end) = {end_id}")
227
- .params(start_id: start_object.neo_id, end_id: other_node.neo_id)
228
- .create("start#{_association_arrow(properties, true)}end").exec
170
+ start_object = @options[:start_object]
171
+ start_object.clear_association_cache
172
+ _session.query(context: @options[:context])
173
+ .match("(start#{match_string(start_object)}), (end#{match_string(other_node)})").where('ID(start) = {start_id} AND ID(end) = {end_id}')
174
+ .params(start_id: start_object.neo_id, end_id: other_node.neo_id)
175
+ .send(create_method, "start#{_association_arrow(properties, true)}end").exec
229
176
 
230
- @association.perform_callback(@options[:start_object], other_node, :after)
231
- #end
177
+ @association.perform_callback(@options[:start_object], other_node, :after)
178
+ # end
232
179
  end
233
180
  end
234
181
 
235
182
  def read_attribute_for_serialization(*args)
236
- to_a.map {|o| o.read_attribute_for_serialization(*args) }
183
+ to_a.map { |o| o.read_attribute_for_serialization(*args) }
237
184
  end
238
185
 
239
186
  # QueryProxy objects act as a representation of a model at the class level so we pass through calls
240
187
  # This allows us to define class functions for reusable query chaining or for end-of-query aggregation/summarizing
241
188
  def method_missing(method_name, *args, &block)
242
189
  if @model && @model.respond_to?(method_name)
243
- args[2] = self if @model.has_association?(method_name) || @model.has_scope?(method_name)
190
+ args[2] = self if @model.association?(method_name) || @model.scope?(method_name)
244
191
  scoping { @model.public_send(method_name, *args, &block) }
245
192
  else
246
193
  super
@@ -252,9 +199,9 @@ module Neo4j
252
199
  end
253
200
 
254
201
  attr_reader :context
255
- attr_reader :node_var
256
202
 
257
203
  protected
204
+
258
205
  # Methods are underscored to prevent conflict with user class methods
259
206
 
260
207
  def _add_params(params)
@@ -268,7 +215,7 @@ module Neo4j
268
215
  def _query_model_as(var)
269
216
  match_arg = if @model
270
217
  label = @model.respond_to?(:mapped_label_name) ? @model.mapped_label_name : @model
271
- { var => label }
218
+ {var => label}
272
219
  else
273
220
  var
274
221
  end
@@ -310,7 +257,7 @@ module Neo4j
310
257
  elsif query_proxy = @options[:query_proxy]
311
258
  query_proxy.node_var || :"node#{_chain_level}"
312
259
  else
313
- raise "Crazy error" # TODO: Better error
260
+ fail 'Crazy error' # TODO: Better error
314
261
  end
315
262
  end
316
263
 
@@ -320,7 +267,7 @@ module Neo4j
320
267
  elsif query_proxy = @options[:query_proxy]
321
268
  query_proxy.query_as(var)
322
269
  else
323
- raise "Crazy error" # TODO: Better error
270
+ fail 'Crazy error' # TODO: Better error
324
271
  end
325
272
  end
326
273
 
@@ -336,6 +283,10 @@ module Neo4j
336
283
 
337
284
  private
338
285
 
286
+ def create_method
287
+ association.unique? ? :create_unique : :create
288
+ end
289
+
339
290
  def build_deeper_query_proxy(method, args)
340
291
  self.dup.tap do |new_query|
341
292
  args.each do |arg|
@@ -359,20 +310,12 @@ module Neo4j
359
310
  result = []
360
311
  if arg.is_a?(Hash)
361
312
  arg.each do |key, value|
362
- if @model && @model.has_association?(key)
363
-
364
- neo_id = value.try(:neo_id) || value
365
- raise ArgumentError, "Invalid value for '#{key}' condition" if not neo_id.is_a?(Integer)
313
+ if @model && @model.association?(key)
314
+ result += links_for_association(key, value, "n#{node_num}")
366
315
 
367
- n_string = "n#{node_num}"
368
- dir = @model.associations[key].direction
369
-
370
- arrow = dir == :out ? '-->' : '<--'
371
- result << [:match, ->(v) { "#{v}#{arrow}(#{n_string})" }]
372
- result << [:where, ->(v) { {"ID(#{n_string})" => neo_id.to_i} }]
373
316
  node_num += 1
374
317
  else
375
- result << [:where, ->(v) { {v => {key => value}}}]
318
+ result << [:where, ->(v) { {v => {key => value}} }]
376
319
  end
377
320
  end
378
321
  elsif arg.is_a?(String)
@@ -382,10 +325,23 @@ module Neo4j
382
325
  end
383
326
  alias_method :links_for_node_where_arg, :links_for_where_arg
384
327
 
328
+ def links_for_association(name, value, n_string)
329
+ neo_id = value.try(:neo_id) || value
330
+ fail ArgumentError, "Invalid value for '#{name}' condition" if not neo_id.is_a?(Integer)
331
+
332
+ dir = @model.associations[name].direction
333
+
334
+ arrow = dir == :out ? '-->' : '<--'
335
+ [
336
+ [:match, ->(v) { "#{v}#{arrow}(#{n_string})" }],
337
+ [:where, ->(_) { {"ID(#{n_string})" => neo_id.to_i} }]
338
+ ]
339
+ end
340
+
385
341
  # We don't accept strings here. If you want to use a string, just use where.
386
342
  def links_for_rel_where_arg(arg)
387
343
  arg.each_with_object([]) do |(key, value), result|
388
- result << [:where, ->(v) {{ rel_identity => { key => value }}}]
344
+ result << [:where, ->(_) { {rel_var => {key => value}} }]
389
345
  end
390
346
  end
391
347
 
@@ -400,4 +356,3 @@ module Neo4j
400
356
  end
401
357
  end
402
358
  end
403
-