neo4j 3.0.0.alpha.9 → 3.0.0.alpha.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +3 -0
- data/Gemfile +2 -1
- data/lib/neo4j.rb +21 -4
- data/lib/neo4j/active_node.rb +5 -35
- data/lib/neo4j/active_node/callbacks.rb +1 -34
- data/lib/neo4j/active_node/has_n/association.rb +35 -16
- data/lib/neo4j/active_node/id_property.rb +1 -1
- data/lib/neo4j/active_node/initialize.rb +2 -2
- data/lib/neo4j/active_node/labels.rb +3 -3
- data/lib/neo4j/active_node/node_wrapper.rb +50 -0
- data/lib/neo4j/active_node/persistence.rb +2 -205
- data/lib/neo4j/active_node/property.rb +1 -194
- data/lib/neo4j/active_node/rels.rb +2 -2
- data/lib/neo4j/active_node/validations.rb +9 -41
- data/lib/neo4j/active_rel.rb +34 -0
- data/lib/neo4j/active_rel/callbacks.rb +13 -0
- data/lib/neo4j/active_rel/initialize.rb +30 -0
- data/lib/neo4j/active_rel/persistence.rb +80 -0
- data/lib/neo4j/active_rel/property.rb +64 -0
- data/lib/neo4j/active_rel/query.rb +66 -0
- data/lib/neo4j/active_rel/rel_wrapper.rb +18 -0
- data/lib/neo4j/active_rel/related_node.rb +42 -0
- data/lib/neo4j/active_rel/validations.rb +9 -0
- data/lib/neo4j/shared.rb +34 -0
- data/lib/neo4j/shared/callbacks.rb +40 -0
- data/lib/neo4j/{active_node → shared}/identity.rb +6 -2
- data/lib/neo4j/shared/persistence.rb +214 -0
- data/lib/neo4j/shared/property.rb +220 -0
- data/lib/neo4j/shared/validations.rb +48 -0
- data/lib/neo4j/version.rb +1 -1
- data/lib/neo4j/wrapper.rb +2 -50
- metadata +18 -5
- data/lib/neo4j/active_node/has_n/nodes.rb +0 -90
- data/lib/neo4j/active_node/quick_query.rb +0 -254
@@ -1,254 +0,0 @@
|
|
1
|
-
module Neo4j
|
2
|
-
module ActiveNode
|
3
|
-
# An abstraction layer to quickly build and return objects from the Neo4j Core Query class.
|
4
|
-
# It auto-increments node and relationship identifiers, uses relationships pre-defined in models to create match methods, and automatically maps
|
5
|
-
# results to collections.
|
6
|
-
class QuickQuery
|
7
|
-
attr_reader :quick_query
|
8
|
-
|
9
|
-
# Initialize sets the values of @node_on_deck and defines @rel_on_deck, among other things.
|
10
|
-
# The objects on deck are the objects implicitly modified when calling a method without specifying an identifier.
|
11
|
-
# They are auto-incremented at stages throughout the class.
|
12
|
-
def initialize(caller, as, caller_class = nil)
|
13
|
-
@caller_class = caller_class || caller
|
14
|
-
@node_on_deck = @return_obj = as.to_sym
|
15
|
-
@current_node_index = 2
|
16
|
-
@current_rel_index = 1
|
17
|
-
@rel_on_deck = nil
|
18
|
-
@return_set = false
|
19
|
-
@caller = caller
|
20
|
-
@quick_query = caller.query_as(as)
|
21
|
-
@identifiers = [@node_on_deck]
|
22
|
-
set_rel_methods(@caller_class)
|
23
|
-
return self
|
24
|
-
end
|
25
|
-
|
26
|
-
# sends the #to_cypher method to the core query class
|
27
|
-
def to_cypher
|
28
|
-
@quick_query.return(@return_obj).to_cypher
|
29
|
-
end
|
30
|
-
|
31
|
-
|
32
|
-
# Creates methods that send cleaned up arguments to the Core Query class
|
33
|
-
# Pass a symbol to specify the target identifier.
|
34
|
-
# Pass a hash to specify match parameters.
|
35
|
-
# Pass a valid cypher string to send directly to Core Query.
|
36
|
-
# If an identifier is not specified, it will apply them to the on-deck node.
|
37
|
-
# @example
|
38
|
-
# Student.qq.where(name: 'chris')
|
39
|
-
# Student.qq.lessons.where(:n2, name: 'history 101')
|
40
|
-
# Student.qq.lessons()
|
41
|
-
CUSTOM_METHODS = %w[where set set_props]
|
42
|
-
|
43
|
-
CUSTOM_METHODS.each do |method|
|
44
|
-
class_eval(%Q{
|
45
|
-
def #{method}(*args)
|
46
|
-
result = prepare(args)
|
47
|
-
final_query(__method__, result)
|
48
|
-
return self
|
49
|
-
end
|
50
|
-
}, __FILE__, __LINE__)
|
51
|
-
end
|
52
|
-
|
53
|
-
|
54
|
-
# Creates methods that send strings directly to Core Query class
|
55
|
-
LITERAL_METHODS = %w[limit skip match offset]
|
56
|
-
|
57
|
-
LITERAL_METHODS.each do |method|
|
58
|
-
class_eval(%Q{
|
59
|
-
def #{method}(s)
|
60
|
-
@quick_query = @quick_query.send(__method__, s)
|
61
|
-
return self
|
62
|
-
end
|
63
|
-
}, __FILE__, __LINE__)
|
64
|
-
end
|
65
|
-
|
66
|
-
# Sends #return to the core query class, does not map to an enumerable.
|
67
|
-
# Assumes the @return_obj if nothing is specified.
|
68
|
-
# if you want distinct, pass boolean true
|
69
|
-
# @example
|
70
|
-
# Student.qq.lessons.return(:n2)
|
71
|
-
# Student.qq.lessons.return(:n2, true)
|
72
|
-
def return(*args)
|
73
|
-
obj_sym = args.select{|el| el.is_a?(Symbol) }.first || @return_obj
|
74
|
-
distinct = args.select{|el| el.is_a?(TrueClass) }.first || false
|
75
|
-
|
76
|
-
r = final_return(obj_sym, distinct)
|
77
|
-
@quick_query = @quick_query.return(r)
|
78
|
-
return self
|
79
|
-
end
|
80
|
-
|
81
|
-
# Returns an enumerable of the query. If return has not been set, will set it to the on_deck node
|
82
|
-
def to_a(distinct = false)
|
83
|
-
@return_set ? result : self.return(distinct).to_a
|
84
|
-
end
|
85
|
-
|
86
|
-
# Same as to_a but with distinct set true
|
87
|
-
def to_a!
|
88
|
-
self.to_a(true)
|
89
|
-
end
|
90
|
-
|
91
|
-
# Set order for return.
|
92
|
-
# @param prop_sym [Symbol] a symbol matching the property on the return class use for order
|
93
|
-
# @param desc_bool [Boolean] boolean to dictate whether to sort descending. Defaults false, use true to descend
|
94
|
-
def order(prop_sym, desc_bool = false)
|
95
|
-
arg = "#{@return_obj}.#{prop_sym.to_s}"
|
96
|
-
end_arg = desc_bool ? arg + ' DESC' : arg
|
97
|
-
@quick_query = @quick_query.order(end_arg)
|
98
|
-
return self
|
99
|
-
end
|
100
|
-
|
101
|
-
private
|
102
|
-
|
103
|
-
def prepare(args)
|
104
|
-
target = args.select{|el| el.is_a?(Symbol) }
|
105
|
-
send_target = target.empty? ? @node_on_deck : target.first
|
106
|
-
result = process_args(args, send_target)
|
107
|
-
end
|
108
|
-
|
109
|
-
def result
|
110
|
-
response = @quick_query.response
|
111
|
-
if response.is_a?(Neo4j::Server::CypherResponse)
|
112
|
-
Neo4j::Session.current.search_result_to_enumerable_first_column(response)
|
113
|
-
else
|
114
|
-
Neo4j::Embedded::ResultWrapper.new(response, @quick_query.to_cypher).map{|x| x[0] }
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
def final_return(return_obj, distinct)
|
119
|
-
@return_set = true
|
120
|
-
distinct ? "distinct #{return_obj.to_s}" : return_obj.to_sym
|
121
|
-
end
|
122
|
-
|
123
|
-
def final_query(method, result)
|
124
|
-
@quick_query = @quick_query.send(method, result)
|
125
|
-
end
|
126
|
-
|
127
|
-
# Creates match methods based on the caller's relationships defined in the model.
|
128
|
-
# It works best when a relationship is defined explicitly with a direction and a receiving/incoming model.
|
129
|
-
# This fires once on initialize, again every time a matcher method is called to build methods for the next step.
|
130
|
-
# The dynamic classes accept the following:
|
131
|
-
# -a symbol to refer to the destination node
|
132
|
-
# -a hash with key :rel_as, value a symbol to act as relationship identifier (otherwise it uses r#{@current_rel_index})
|
133
|
-
# -a hash with key :rel_where containing other hashes of {parameter: value} to specify relationship conditions
|
134
|
-
# -hashes of {parameter: value} that specify conditions for the destination nodes
|
135
|
-
# @example
|
136
|
-
# Student.qq.lessons
|
137
|
-
# Student.qq.lessons(rel_as: :student_status)
|
138
|
-
# Student.qq.lessons(rel_as: :student_status, rel_where: { grade: 'b-' })
|
139
|
-
# Student.qq.lessons(rel_as: :student_status, rel_where: { grade: 'b-' }, :lessinzzz, class: 'history 101').teachers
|
140
|
-
def set_rel_methods(caller_class)
|
141
|
-
caller_class._decl_rels.each { |k,v|
|
142
|
-
if v.target_class.nil?
|
143
|
-
class_eval(%Q{
|
144
|
-
def #{k}(*args)
|
145
|
-
process_rel(args, "#{v.rel_type}")
|
146
|
-
return self
|
147
|
-
end}, __FILE__, __LINE__)
|
148
|
-
else
|
149
|
-
class_eval(%Q{
|
150
|
-
def #{k}(*args)
|
151
|
-
process_rel(args, "#{v.rel_type}", "#{v.target_class.name}")
|
152
|
-
return self
|
153
|
-
end}, __FILE__, __LINE__)
|
154
|
-
end
|
155
|
-
}
|
156
|
-
end
|
157
|
-
|
158
|
-
# Called when a matcher method is called
|
159
|
-
# args are the arguments sent along with the matcher method
|
160
|
-
# rel_type is the defined relationship type
|
161
|
-
# right_label is the label to use for the destination, if available. It's the "right label" cause it's on the right side... get it?
|
162
|
-
# A label can only be used if the model defines the destination class explicitly.
|
163
|
-
def process_rel(args, rel_type, right_label = nil)
|
164
|
-
from_node = @node_on_deck
|
165
|
-
hashes, strings = setup_rel_args(args)
|
166
|
-
set_on_deck(args)
|
167
|
-
|
168
|
-
hashes = process_rel_hashes(hashes) unless hashes.nil?
|
169
|
-
end_args = process_args([hashes] + strings, @node_on_deck) unless hashes.nil? && strings.nil?
|
170
|
-
|
171
|
-
destination = right_label.nil? ? @node_on_deck : "(#{@node_on_deck}:#{right_label})"
|
172
|
-
@quick_query = @quick_query.match("#{from_node}-[#{@rel_on_deck}:`#{rel_type}`]-#{destination}")
|
173
|
-
@quick_query = @quick_query.where(end_args) unless end_args.nil?
|
174
|
-
set_rel_methods(right_label.constantize) unless right_label.nil?
|
175
|
-
|
176
|
-
return self
|
177
|
-
end
|
178
|
-
|
179
|
-
# Prepares arguments passed with the relationship matcher. It finds hashes, which contain properties and values, and strings, which
|
180
|
-
# which literal cypher phrases.
|
181
|
-
def setup_rel_args(args)
|
182
|
-
hashes = args.select{|el| el.is_a?(Hash) }.first
|
183
|
-
strings = args.select{|el| el.is_a?(String) }
|
184
|
-
return hashes, strings
|
185
|
-
end
|
186
|
-
|
187
|
-
# Queues up the new node and relationship. This is only used during process_rel.
|
188
|
-
def set_on_deck(args)
|
189
|
-
@node_on_deck = @return_obj = node_as(args.select{|el| el.is_a?(Symbol) }.first)
|
190
|
-
@rel_on_deck = new_rel_id
|
191
|
-
@identifiers.push([@node_on_deck, @rel_on_deck])
|
192
|
-
end
|
193
|
-
|
194
|
-
# Prepares relationship-specific hashes found during the setup_rel_args method. Removes anything it finds from the hash and sends it back.
|
195
|
-
def process_rel_hashes(hashes)
|
196
|
-
@rel_on_deck = set_rel_as(hashes)
|
197
|
-
@identifiers.push @rel_on_deck
|
198
|
-
|
199
|
-
set_rel_where(hashes)
|
200
|
-
|
201
|
-
hashes.delete_if{|k,v| k == :rel_as || k == :rel_where }
|
202
|
-
end
|
203
|
-
|
204
|
-
# Creates a new node identifier
|
205
|
-
def new_node_id
|
206
|
-
n = "n#{@current_node_index}"
|
207
|
-
@current_node_index += 1
|
208
|
-
return n
|
209
|
-
end
|
210
|
-
|
211
|
-
# Creates a new relationship identifier
|
212
|
-
def new_rel_id
|
213
|
-
r = "r#{@current_rel_index}"
|
214
|
-
@current_rel_index += 1
|
215
|
-
return r
|
216
|
-
end
|
217
|
-
|
218
|
-
def node_as(node_as)
|
219
|
-
node_as.nil? ? new_node_id : node_as.to_sym
|
220
|
-
end
|
221
|
-
|
222
|
-
def set_rel_as(h)
|
223
|
-
h.has_key?(:rel_as) ? h[:rel_as] : new_rel_id
|
224
|
-
end
|
225
|
-
|
226
|
-
def set_rel_where(h)
|
227
|
-
if h.has_key?(:rel_where)
|
228
|
-
@quick_query = @quick_query.where(Hash[@rel_on_deck => h[:rel_where]])
|
229
|
-
end
|
230
|
-
end
|
231
|
-
|
232
|
-
# Utility method used to split up passed values and fix syntax to match Neo4j Core Query class
|
233
|
-
def process_args(args, where_target)
|
234
|
-
end_args = []
|
235
|
-
args.each do |arg|
|
236
|
-
if arg.is_a?(String)
|
237
|
-
end_args.push process_string(arg, where_target)
|
238
|
-
elsif arg.is_a?(Symbol)
|
239
|
-
@node_on_deck = arg
|
240
|
-
@return_obj = arg if @return_obj.nil?
|
241
|
-
elsif arg.is_a?(Hash)
|
242
|
-
end_args.push Hash[where_target => arg] unless arg.empty?
|
243
|
-
end
|
244
|
-
end
|
245
|
-
return end_args
|
246
|
-
end
|
247
|
-
|
248
|
-
# Attempts to determine whether the passed string already contains a node/rel identifier or if it needs one prepended.
|
249
|
-
def process_string(arg, where_target)
|
250
|
-
@identifiers.include?(arg.split('.').first.to_sym) ? arg : "#{where_target}.#{arg}"
|
251
|
-
end
|
252
|
-
end
|
253
|
-
end
|
254
|
-
end
|