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.
- checksums.yaml +4 -4
- data/CHANGELOG +10 -0
- data/Gemfile +6 -16
- data/README.md +5 -2
- data/bin/neo4j-jars +6 -6
- data/lib/neo4j.rb +13 -9
- data/lib/neo4j/active_node.rb +9 -9
- data/lib/neo4j/active_node/dependent.rb +11 -0
- data/lib/neo4j/active_node/dependent/association_methods.rb +28 -0
- data/lib/neo4j/active_node/dependent/query_proxy_methods.rb +48 -0
- data/lib/neo4j/active_node/has_n.rb +124 -112
- data/lib/neo4j/active_node/has_n/association.rb +45 -30
- data/lib/neo4j/active_node/id_property.rb +22 -19
- data/lib/neo4j/active_node/initialize.rb +2 -4
- data/lib/neo4j/active_node/labels.rb +23 -22
- data/lib/neo4j/active_node/node_wrapper.rb +5 -8
- data/lib/neo4j/active_node/orm_adapter.rb +2 -4
- data/lib/neo4j/active_node/persistence.rb +5 -10
- data/lib/neo4j/active_node/property.rb +3 -4
- data/lib/neo4j/active_node/query.rb +27 -6
- data/lib/neo4j/active_node/query/query_proxy.rb +65 -110
- data/lib/neo4j/active_node/query/query_proxy_enumerable.rb +67 -0
- data/lib/neo4j/active_node/query/query_proxy_find_in_batches.rb +0 -1
- data/lib/neo4j/active_node/query/query_proxy_methods.rb +29 -28
- data/lib/neo4j/active_node/query_methods.rb +6 -6
- data/lib/neo4j/active_node/reflection.rb +3 -2
- data/lib/neo4j/active_node/rels.rb +1 -1
- data/lib/neo4j/active_node/scope.rb +13 -8
- data/lib/neo4j/active_node/validations.rb +5 -6
- data/lib/neo4j/active_rel.rb +1 -2
- data/lib/neo4j/active_rel/callbacks.rb +3 -3
- data/lib/neo4j/active_rel/persistence.rb +9 -7
- data/lib/neo4j/active_rel/property.rb +12 -4
- data/lib/neo4j/active_rel/query.rb +6 -8
- data/lib/neo4j/active_rel/rel_wrapper.rb +0 -2
- data/lib/neo4j/active_rel/related_node.rb +4 -5
- data/lib/neo4j/active_rel/types.rb +4 -6
- data/lib/neo4j/active_rel/validations.rb +0 -1
- data/lib/neo4j/config.rb +11 -23
- data/lib/neo4j/core/query.rb +1 -1
- data/lib/neo4j/migration.rb +17 -18
- data/lib/neo4j/paginated.rb +4 -4
- data/lib/neo4j/railtie.rb +19 -19
- data/lib/neo4j/shared.rb +7 -3
- data/lib/neo4j/shared/callbacks.rb +15 -4
- data/lib/neo4j/shared/identity.rb +2 -2
- data/lib/neo4j/shared/persistence.rb +10 -21
- data/lib/neo4j/shared/property.rb +17 -30
- data/lib/neo4j/shared/rel_type_converters.rb +1 -3
- data/lib/neo4j/shared/type_converters.rb +13 -25
- data/lib/neo4j/shared/validations.rb +3 -3
- data/lib/neo4j/tasks/migration.rake +7 -7
- data/lib/neo4j/type_converters.rb +1 -1
- data/lib/neo4j/version.rb +1 -1
- data/lib/rails/generators/neo4j/model/model_generator.rb +16 -12
- data/lib/rails/generators/neo4j_generator.rb +18 -18
- data/neo4j.gemspec +22 -18
- 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
|
-
|
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
|
-
# There
|
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 = {}
|
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
|
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?
|
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.
|
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(
|
20
|
-
self.class.query_as(
|
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(%
|
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
|
-
|
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
|
-
|
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
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
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(
|
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
|
-
|
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
|
-
|
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
|
-
|
165
|
+
# Neo4j::Transaction.run do
|
166
|
+
other_node.save unless other_node.neo_id
|
220
167
|
|
221
|
-
|
168
|
+
return false if @association.perform_callback(@options[:start_object], other_node, :before) == false
|
222
169
|
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
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
|
-
|
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.
|
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
|
-
{
|
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
|
-
|
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
|
-
|
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.
|
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, ->(
|
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
|
-
|