neo4j 4.0.0 → 4.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
|