active-orient 0.79 → 0.80
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.graphs.txt.swp +0 -0
- data/Gemfile +2 -6
- data/README.md +29 -27
- data/VERSION +1 -1
- data/active-orient.gemspec +4 -3
- data/bin/active-orient-console +18 -6
- data/changelog.md +60 -0
- data/config/connect.yml +8 -8
- data/examples/books.rb +134 -97
- data/graphs.txt +70 -0
- data/lib/active-orient.rb +2 -0
- data/lib/base.rb +38 -17
- data/lib/base_properties.rb +15 -14
- data/lib/class_utils.rb +11 -50
- data/lib/database_utils.rb +23 -22
- data/lib/init.rb +4 -3
- data/lib/model/custom.rb +7 -4
- data/lib/model/e.rb +6 -0
- data/lib/model/edge.rb +74 -30
- data/lib/model/the_class.rb +181 -131
- data/lib/model/the_record.rb +115 -68
- data/lib/model/vertex.rb +261 -126
- data/lib/other.rb +93 -41
- data/lib/rest/change.rb +23 -20
- data/lib/rest/create.rb +71 -63
- data/lib/rest/delete.rb +80 -64
- data/lib/rest/operations.rb +79 -68
- data/lib/rest/read.rb +42 -24
- data/lib/rest/rest.rb +38 -30
- data/lib/support/conversions.rb +42 -0
- data/lib/support/default_formatter.rb +7 -0
- data/lib/support/errors.rb +41 -0
- data/lib/support/orient.rb +167 -58
- data/lib/support/orientquery.rb +526 -348
- data/lib/support/query.rb +92 -0
- metadata +34 -18
- data/examples/test_commands.rb +0 -97
- data/examples/test_commands_2.rb +0 -59
- data/examples/test_commands_3.rb +0 -55
- data/examples/test_commands_4.rb +0 -33
- data/examples/time_graph.md +0 -162
data/lib/support/orientquery.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'active_support/inflector'
|
2
|
+
|
2
3
|
module OrientSupport
|
3
4
|
module Support
|
4
5
|
|
@@ -16,10 +17,13 @@ _Usecase:_
|
|
16
17
|
=end
|
17
18
|
|
18
19
|
#
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
def compose_where *arg , &b
|
21
|
+
arg = arg.flatten.compact
|
22
|
+
|
23
|
+
unless arg.blank?
|
24
|
+
g= generate_sql_list( arg , &b)
|
25
|
+
"where #{g}" unless g.empty?
|
26
|
+
end
|
23
27
|
end
|
24
28
|
|
25
29
|
=begin
|
@@ -28,442 +32,616 @@ designs a list of "Key = Value" pairs combined by "and" or the binding provide
|
|
28
32
|
=> "where = 25 and upper = '65'"
|
29
33
|
ORD.generate_sql_list( con_id: 25 , symbol: :G) { ',' }
|
30
34
|
=> "con_id = 25 , symbol = 'G'"
|
35
|
+
|
36
|
+
If »NULL« should be addressed, { key: nil } is translated to "key = NULL" (used by set: in update and upsert),
|
37
|
+
{ key: [nil] } is translated to "key is NULL" ( used by where )
|
38
|
+
|
31
39
|
=end
|
32
|
-
def generate_sql_list attributes = {},
|
40
|
+
def generate_sql_list attributes = {}, &b
|
33
41
|
fill = block_given? ? yield : 'and'
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
when Numeric
|
41
|
-
"#{key} = #{value}"
|
42
|
-
when ::Array
|
43
|
-
"#{key} in [#{value.to_orient}]"
|
44
|
-
when Range
|
45
|
-
"#{key} between #{value.first} and #{value.last} "
|
46
|
-
when DateTime
|
47
|
-
"#{key} = date(\'#{value.strftime("%Y%m%d%H%M%S")}\',\'yyyyMMddHHmmss\')"
|
48
|
-
when Date
|
49
|
-
"#{key} = date(\'#{value.to_s}\',\'yyyy-MM-dd\')"
|
50
|
-
else # String, Symbol, Time, Trueclass, Falseclass ...
|
51
|
-
"#{key} = \'#{value.to_s}\'"
|
52
|
-
end
|
53
|
-
end.join(" #{fill} ")
|
42
|
+
case attributes
|
43
|
+
when ::Hash
|
44
|
+
attributes.map do |key, value|
|
45
|
+
case value
|
46
|
+
when nil
|
47
|
+
"#{key} = NULL"
|
54
48
|
when ::Array
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
49
|
+
if value == [nil]
|
50
|
+
"#{key} is NULL"
|
51
|
+
else
|
52
|
+
"#{key} in #{value.to_orient}"
|
53
|
+
end
|
54
|
+
when Range
|
55
|
+
"#{key} between #{value.first} and #{value.last} "
|
56
|
+
else # String, Symbol, Time, Trueclass, Falseclass ...
|
57
|
+
"#{key} = #{value.to_or}"
|
58
|
+
end
|
59
|
+
end.join(" #{fill} ")
|
60
|
+
when ::Array
|
61
|
+
attributes.map{|y| generate_sql_list y, &b }.join( " #{fill} " )
|
62
|
+
when String
|
63
|
+
attributes
|
64
|
+
when Symbol, Numeric
|
65
|
+
attributes.to_s
|
66
|
+
end
|
59
67
|
end
|
60
|
-
end
|
61
68
|
|
62
69
|
|
70
|
+
# used both in OrientQuery and MatchConnect
|
71
|
+
# while and where depend on @q, a struct
|
72
|
+
def while_s value=nil # :nodoc:
|
73
|
+
if value.present?
|
74
|
+
@q[:while] << value
|
75
|
+
self
|
76
|
+
elsif @q[:while].present?
|
77
|
+
"while #{ generate_sql_list( @q[:while] ) }"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
def where value=nil # :nodoc:
|
81
|
+
if value.present?
|
82
|
+
if value.is_a?( Hash ) && value.size >1
|
83
|
+
value.each {| a,b| where( {a => b} ) }
|
84
|
+
else
|
85
|
+
@q[:where] << value
|
86
|
+
end
|
87
|
+
self
|
88
|
+
elsif @q[:where].present?
|
89
|
+
"where #{ generate_sql_list( @q[:where] ){ @fill || 'and' } }"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def as a=nil
|
94
|
+
if a
|
95
|
+
@q[:as] = a # subsequent calls overwrite older entries
|
96
|
+
else
|
97
|
+
if @q[:as].blank?
|
98
|
+
nil
|
99
|
+
else
|
100
|
+
"as: #{ @q[:as] }"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end # module
|
105
|
+
|
106
|
+
######################## MatchConnection ###############################
|
107
|
+
|
108
|
+
MatchAttributes = Struct.new(:edge, :direction, :as, :count, :where, :while, :max_depth , :depth_alias, :path_alias, :optional )
|
109
|
+
|
110
|
+
# where and while can be composed incremental
|
111
|
+
# direction, as, connect and edge cannot be changed after initialisation
|
63
112
|
class MatchConnection
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
113
|
+
include Support
|
114
|
+
|
115
|
+
def initialize edge= nil, direction: :both, as: nil, count: 1, **args
|
116
|
+
|
117
|
+
the_edge = edge.is_a?( Class ) ? edge.ref_name : edge.to_s unless edge.nil? || edge == E
|
118
|
+
@q = MatchAttributes.new the_edge , # class
|
119
|
+
direction, # may be :both, :in, :out
|
120
|
+
as, # a string
|
121
|
+
count, # a number
|
122
|
+
args[:where],
|
123
|
+
args[:while],
|
124
|
+
args[:max_depth],
|
125
|
+
args[:depth_alias], # not implemented
|
126
|
+
args[:path_alias], # not implemented
|
127
|
+
args[:optional] # not implemented
|
70
128
|
end
|
71
129
|
|
72
130
|
def direction= dir
|
73
|
-
@direction = dir
|
131
|
+
@q[:direction] = dir
|
74
132
|
end
|
75
133
|
|
76
134
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
135
|
+
def direction
|
136
|
+
fillup = @q[:edge].present? ? @q[:edge] : ''
|
137
|
+
case @q[:direction]
|
138
|
+
when :both
|
139
|
+
".both(#{fillup})"
|
140
|
+
when :in
|
141
|
+
".in(#{fillup})"
|
142
|
+
when :out
|
143
|
+
".out(#{fillup})"
|
144
|
+
when :both_vertex, :bothV
|
145
|
+
".bothV()"
|
146
|
+
when :out_vertex, :outV
|
147
|
+
".outV()"
|
148
|
+
when :in_vertex, :inV
|
149
|
+
".inV()"
|
150
|
+
when :both_edge, :bothE
|
151
|
+
".bothE(#{fillup})"
|
152
|
+
when :out_edge, :outE
|
153
|
+
".outE(#{fillup})"
|
154
|
+
when :in_edge, :outE
|
155
|
+
".inE(#{fillup})"
|
156
|
+
end
|
89
157
|
|
90
|
-
|
91
|
-
ministatement = @as.present? ? "{ as: #{@as} } " : ""
|
92
|
-
(1 .. @count).map{|x| direction }.join("{}") << ministatement
|
158
|
+
end
|
93
159
|
|
94
|
-
|
95
|
-
|
96
|
-
|
160
|
+
def count c=nil
|
161
|
+
if c
|
162
|
+
@q[:count] = c
|
163
|
+
else
|
164
|
+
@q[:count]
|
165
|
+
end
|
166
|
+
end
|
97
167
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
@match_class = match_class
|
111
|
-
@as = match_class.pluralize if match_class.is_a? String
|
112
|
-
|
113
|
-
args.each do |k, v|
|
114
|
-
case k
|
115
|
-
when :as
|
116
|
-
@as = v
|
117
|
-
when :while
|
118
|
-
@while << v
|
119
|
-
when :where
|
120
|
-
@where << v
|
121
|
-
when :class
|
122
|
-
@match_class = v
|
123
|
-
@as = v.pluralize
|
124
|
-
else
|
125
|
-
self.send k, v
|
126
|
-
end
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
def while_s
|
131
|
-
compose_where( @while ).gsub( /where/, 'while:(' )<< ")" unless @while.blank?
|
132
|
-
end
|
168
|
+
def max_depth d=nil
|
169
|
+
if d.nil?
|
170
|
+
@q[:max_depth].present? ? "maxDepth: #{@q[:max_depth] }" : nil
|
171
|
+
else
|
172
|
+
@q[:max_depth] = d
|
173
|
+
end
|
174
|
+
end
|
175
|
+
def edge
|
176
|
+
@q[:edge]
|
177
|
+
end
|
133
178
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
def maxdepth=x
|
142
|
-
@maxdepth = x
|
143
|
-
end
|
179
|
+
def compose
|
180
|
+
where_statement =( where.nil? || where.size <5 ) ? nil : "where: ( #{ generate_sql_list( @q[:where] ) })"
|
181
|
+
while_statement =( while_s.nil? || while_s.size <5) ? nil : "while: ( #{ generate_sql_list( @q[:while] )})"
|
182
|
+
|
183
|
+
ministatement = "{"+ [ as, where_statement, while_statement, max_depth].compact.join(', ') + "}"
|
184
|
+
ministatement = "" if ministatement=="{}"
|
144
185
|
|
145
|
-
|
146
|
-
@misc << method.to_s << generate_sql_list(arg)
|
147
|
-
end
|
186
|
+
(1 .. count).map{|x| direction }.join("") + ministatement
|
148
187
|
|
149
|
-
def misc
|
150
|
-
@misc.join(' ') unless @misc.empty?
|
151
|
-
end
|
152
|
-
# used for the first compose-statement of a compose-query
|
153
|
-
def compose_simple
|
154
|
-
'{'+ [ "class: #{@match_class}",
|
155
|
-
"as: #{@as}" ,
|
156
|
-
where_s ].compact.join(', ') + '}'
|
157
188
|
end
|
189
|
+
alias :to_s :compose
|
190
|
+
|
191
|
+
end # class
|
158
192
|
|
159
|
-
def compose
|
160
193
|
|
161
|
-
|
162
|
-
"as: #{@as}" ,
|
163
|
-
where_s,
|
164
|
-
while_s,
|
165
|
-
@maxdepth >0 ? "maxdepth: #{maxdepth}": nil ].compact.join(', ')+'}'
|
166
|
-
end
|
167
|
-
alias :to_s :compose
|
168
|
-
end
|
194
|
+
######################## MatchStatement ################################
|
169
195
|
|
170
|
-
|
196
|
+
MatchSAttributes = Struct.new(:match_class, :as, :where )
|
197
|
+
class MatchStatement
|
171
198
|
include Support
|
199
|
+
def initialize match_class, as: 0, **args
|
200
|
+
reduce_class = ->(c){ c.is_a?(Class) ? c.ref_name : c.to_s }
|
172
201
|
|
202
|
+
@q = MatchSAttributes.new( reduce_class[match_class], # class
|
203
|
+
as.respond_to?(:zero?) && as.zero? ? reduce_class[match_class].pluralize : as ,
|
204
|
+
args[ :where ])
|
173
205
|
|
174
|
-
|
175
|
-
attr_accessor :let
|
176
|
-
attr_accessor :projection
|
177
|
-
attr_accessor :order
|
178
|
-
attr_accessor :db
|
179
|
-
attr_accessor :match_statements
|
180
|
-
|
181
|
-
def initialize **args
|
182
|
-
@projection = []
|
183
|
-
@misc = []
|
184
|
-
@let = []
|
185
|
-
@where = []
|
186
|
-
@order = []
|
187
|
-
@aliases = []
|
188
|
-
@match_statements = []
|
189
|
-
@class = nil
|
190
|
-
@return = nil
|
191
|
-
@db = nil
|
192
|
-
@kind = 'select'
|
193
|
-
args.each do |k, v|
|
194
|
-
case k
|
195
|
-
when :projection
|
196
|
-
@projection << v
|
197
|
-
when :let
|
198
|
-
@let << v
|
199
|
-
when :order
|
200
|
-
@order << v
|
201
|
-
when :where
|
202
|
-
@where << v
|
203
|
-
when :kind
|
204
|
-
@kind = v
|
205
|
-
when :start
|
206
|
-
@match_statements[0] = MatchStatement.new **v
|
207
|
-
# @match_statements[1] = MatchConnection.new
|
208
|
-
when :connection
|
209
|
-
@match_statements[1] = MatchConnection.new **v
|
210
|
-
when :return
|
211
|
-
@aliases << v
|
212
|
-
else
|
213
|
-
self.send k, v
|
214
|
-
end
|
215
|
-
end
|
206
|
+
@query_stack = [ self ]
|
216
207
|
end
|
217
208
|
|
209
|
+
def match_alias
|
210
|
+
"as: #{@q[:as]}"
|
211
|
+
end
|
218
212
|
|
219
|
-
=begin
|
220
|
-
where: "r > 9" --> where r > 9
|
221
|
-
where: {a: 9, b: 's'} --> where a = 9 and b = 's'
|
222
|
-
where:[{ a: 2} , 'b > 3',{ c: 'ufz' }] --> where a = 2 and b > 3 and c = 'ufz'
|
223
|
-
=end
|
224
|
-
def method_missing method, *arg, &b # :nodoc:
|
225
|
-
@misc << method.to_s << generate_sql_list(arg)
|
226
|
-
self.to_s # return compiled result
|
227
|
-
end
|
228
213
|
|
229
214
|
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
215
|
+
# used for the first compose-statement of a compose-query
|
216
|
+
def compose_simple
|
217
|
+
where_statement = where.is_a?(String) && where.size <3 ? nil : "where: ( #{ generate_sql_list( @q[:where] ) })"
|
218
|
+
'{'+ [ "class: #{@q[:match_class]}", as , where_statement].compact.join(', ') + '}'
|
219
|
+
end
|
234
220
|
|
235
|
-
def subquery # :nodoc:
|
236
|
-
nil
|
237
|
-
end
|
238
221
|
|
222
|
+
def << connection
|
223
|
+
@query_stack << connection
|
224
|
+
self # return MatchStatement
|
225
|
+
end
|
226
|
+
#
|
227
|
+
def compile &b
|
228
|
+
"match " + @query_stack.map( &:to_s ).join + return_statement( &b )
|
229
|
+
end
|
239
230
|
|
240
231
|
|
241
|
-
|
242
|
-
|
232
|
+
# executes the standard-case.
|
233
|
+
# returns
|
234
|
+
# * as: :hash : an array of hashes
|
235
|
+
# * as: :array : an array of hash-values
|
236
|
+
# * as :flatten: a simple array of hash-values
|
237
|
+
#
|
238
|
+
# The optional block is used to customize the output.
|
239
|
+
# All previously defiend »as«-Statements are provided though the control variable.
|
240
|
+
#
|
241
|
+
# Background
|
242
|
+
# A match query "Match {class aaa, as: 'aa'} return aa "
|
243
|
+
#
|
244
|
+
# returns [ aa: { result of the query, a Vertex or a value-item }, aa: {}...}, ...] ]
|
245
|
+
# (The standard case)
|
246
|
+
#
|
247
|
+
# A match query "Match {class aaa, as: 'aa'} return aa.name "
|
248
|
+
# returns [ aa.name: { name }, aa.name: { name }., ...] ]
|
249
|
+
#
|
250
|
+
# Now, execute( as: :flatten){ "aa.name" } returns
|
251
|
+
# [name1, name2 ,. ...]
|
252
|
+
#
|
253
|
+
#
|
254
|
+
# Return statements (examples from https://orientdb.org/docs/3.0.x/sql/SQL-Match.html)
|
255
|
+
# "person.name as name, friendship.since as since, friend.name as friend"
|
256
|
+
#
|
257
|
+
# " person.name + \" is a friend of \" + friend.name as friends"
|
258
|
+
#
|
259
|
+
# "$matches"
|
260
|
+
# "$elements"
|
261
|
+
# "$paths"
|
262
|
+
# "$pathElements"
|
263
|
+
#
|
264
|
+
#
|
265
|
+
#
|
266
|
+
def execute as: :hash, &b
|
267
|
+
r = V.db.execute{ compile &b }
|
268
|
+
case as
|
269
|
+
when :hash
|
270
|
+
r
|
271
|
+
when :array
|
272
|
+
r.map{|y| y.values}
|
273
|
+
when :flatten
|
274
|
+
r.map{|y| y.values}.orient_flatten
|
275
|
+
else
|
276
|
+
raise ArgumentError, "Specify parameter «as:» with :hash, :array, :flatten"
|
277
|
+
end
|
278
|
+
end
|
279
|
+
# def compose
|
280
|
+
#
|
281
|
+
# '{'+ [ "class: #{@q[:match_class]}",
|
282
|
+
# "as: #{@as}" , where, while_s,
|
283
|
+
# @maxdepth >0 ? "maxdepth: #{maxdepth}": nil ].compact.join(', ')+'}'
|
284
|
+
# end
|
285
|
+
|
286
|
+
alias :to_s :compose_simple
|
287
|
+
|
288
|
+
|
289
|
+
## return_statement
|
290
|
+
#
|
291
|
+
# summarizes defined as-statements ready to be included as last parameter
|
292
|
+
# in the match-statement-stack
|
293
|
+
#
|
294
|
+
# They can be modified through a block.
|
295
|
+
#
|
296
|
+
# i.e
|
297
|
+
#
|
298
|
+
# t= TestQuery.match( where: {a: 9, b: 's'}, as: nil ) << E.connect("<-", as: :test)
|
299
|
+
# t.return_statement{|y| "#{y.last}.name"}
|
300
|
+
#
|
301
|
+
# =>> " return test.name"
|
302
|
+
#
|
303
|
+
#return_statement is always called through compile
|
304
|
+
#
|
305
|
+
# t.compile{|y| "#{y.last}.name"}
|
306
|
+
|
307
|
+
private
|
308
|
+
def return_statement
|
309
|
+
resolve_as = ->{ @query_stack.map{|s| s.as.split(':').last unless s.as.nil? }.compact }
|
310
|
+
" return " + statement = if block_given?
|
311
|
+
a= yield resolve_as[]
|
312
|
+
a.is_a?(Array) ? a.join(', ') : a
|
313
|
+
else
|
314
|
+
resolve_as[].join(', ')
|
315
|
+
end
|
243
316
|
|
244
|
-
|
317
|
+
|
318
|
+
end
|
319
|
+
|
320
|
+
end # class
|
245
321
|
|
246
|
-
A Match-Query alwas has an Entry-Stratement and maybe other Statements.
|
247
|
-
They are connected via " -> " (outE), "<-" (inE) or "--" (both).
|
248
322
|
|
249
|
-
|
323
|
+
######################## OrientQuery ###################################
|
250
324
|
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
325
|
+
QueryAttributes = Struct.new( :kind, :projection, :where, :let, :order, :while, :misc,
|
326
|
+
:class, :return, :aliases, :database,
|
327
|
+
:set, :remove, :group, :skip, :limit, :unwind )
|
328
|
+
|
329
|
+
class OrientQuery
|
330
|
+
include Support
|
257
331
|
|
258
|
-
The method returns the OrientSupport::MatchConnection object, which can be modified further.
|
259
|
-
It is compiled by calling compose
|
260
|
-
=end
|
261
332
|
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
333
|
+
#
|
334
|
+
def initialize **args
|
335
|
+
@q = QueryAttributes.new args[:kind] || 'select' ,
|
336
|
+
[], # :projection
|
337
|
+
[], # :where ,
|
338
|
+
[], # :let ,
|
339
|
+
[], # :order,
|
340
|
+
[], # :while,
|
341
|
+
[] , # misc
|
342
|
+
'', # class
|
343
|
+
'', # return
|
344
|
+
[], # aliases
|
345
|
+
'', # database
|
346
|
+
[], #set,
|
347
|
+
[] # remove
|
348
|
+
args.each{|k,v| send k, v}
|
349
|
+
@fill = block_given? ? yield : 'and'
|
266
350
|
end
|
351
|
+
|
267
352
|
|
268
353
|
=begin
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
354
|
+
where: "r > 9" --> where r > 9
|
355
|
+
where: {a: 9, b: 's'} --> where a = 9 and b = 's'
|
356
|
+
where:[{ a: 2} , 'b > 3',{ c: 'ufz' }] --> where a = 2 and b > 3 and c = 'ufz'
|
357
|
+
=end
|
358
|
+
def method_missing method, *arg, &b # :nodoc:
|
359
|
+
if method ==:while || method=='while'
|
360
|
+
while_s arg.first
|
361
|
+
else
|
362
|
+
@q[:misc] << method.to_s << generate_sql_list(arg)
|
363
|
+
end
|
364
|
+
self
|
365
|
+
end
|
278
366
|
|
279
|
-
|
367
|
+
def misc # :nodoc:
|
368
|
+
@q[:misc].join(' ') unless @q[:misc].empty?
|
369
|
+
end
|
280
370
|
|
281
|
-
|
282
|
-
|
371
|
+
def subquery # :nodoc:
|
372
|
+
nil
|
373
|
+
end
|
283
374
|
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
375
|
+
|
376
|
+
def kind value=nil
|
377
|
+
if value.present?
|
378
|
+
@q[:kind] = value
|
379
|
+
self
|
380
|
+
else
|
381
|
+
@q[:kind]
|
382
|
+
end
|
383
|
+
end
|
289
384
|
=begin
|
290
385
|
Output the compiled query
|
291
386
|
Parameter: destination (rest, batch )
|
292
387
|
If the query is submitted via the REST-Interface (as get-command), the limit parameter is extracted.
|
293
388
|
=end
|
294
389
|
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
390
|
+
def compose(destination: :batch)
|
391
|
+
if kind.to_sym == :update
|
392
|
+
return_statement = "return after " + ( @q[:aliases].empty? ? "$current" : @q[:aliases].first.to_s)
|
393
|
+
[ 'update', target, set, remove, return_statement , where, limit ].compact.join(' ')
|
394
|
+
elsif kind.to_sym == :update!
|
395
|
+
[ 'update', target, set, where, limit, misc ].compact.join(' ')
|
396
|
+
elsif kind.to_sym == :create
|
397
|
+
[ "CREATE VERTEX", target, set ].compact.join(' ')
|
398
|
+
# [ kind, target, set, return_statement ,where, limit, misc ].compact.join(' ')
|
399
|
+
elsif kind.to_sym == :upsert
|
400
|
+
return_statement = "return after " + ( @q[:aliases].empty? ? "$current" : @q[:aliases].first.to_s)
|
401
|
+
[ "update", target, set,"upsert", return_statement , where, limit, misc ].compact.join(' ')
|
402
|
+
#[ kind, where, return_statement ].compact.join(' ')
|
403
|
+
elsif destination == :rest
|
404
|
+
[ kind, projection, from, let, where, subquery, misc, order, group_by, unwind, skip].compact.join(' ')
|
405
|
+
else
|
406
|
+
[ kind, projection, from, let, where, subquery, while_s, misc, order, group_by, limit, unwind, skip].compact.join(' ')
|
301
407
|
end
|
302
|
-
elsif @kind.to_sym == :update
|
303
|
-
return_statement = "return after " + ( @aliases.empty? ? "$this" : @aliases.first.to_s)
|
304
|
-
[ @kind, @database, misc, where_s, return_statement ].compact.join(' ')
|
305
|
-
elsif destination == :rest
|
306
|
-
[@kind, projection_s, from, let_s, where_s, subquery, misc, order_s, group_by, unwind, skip].compact.join(' ')
|
307
|
-
else
|
308
|
-
[@kind, projection_s, from, let_s, where_s, subquery, misc, order_s, group_by, limit, unwind, skip].compact.join(' ')
|
309
408
|
end
|
310
|
-
|
311
|
-
alias :to_s :compose
|
409
|
+
alias :to_s :compose
|
312
410
|
|
313
|
-
=begin
|
314
|
-
from can either be a Databaseclass to operate on or a Subquery providing data to query further
|
315
|
-
=end
|
316
411
|
|
412
|
+
def to_or
|
413
|
+
compose.to_or
|
414
|
+
end
|
317
415
|
|
318
|
-
|
319
|
-
|
320
|
-
|
416
|
+
def target arg = nil
|
417
|
+
if arg.present?
|
418
|
+
@q[:database] = arg
|
419
|
+
self # return query-object
|
420
|
+
elsif @q[:database].present?
|
421
|
+
the_argument = @q[:database]
|
422
|
+
case @q[:database]
|
321
423
|
when ActiveOrient::Model # a single record
|
322
|
-
|
323
|
-
when
|
324
|
-
' ( '+
|
424
|
+
the_argument.rrid
|
425
|
+
when self.class # result of a query
|
426
|
+
' ( '+ the_argument.compose + ' ) '
|
325
427
|
when Class
|
326
|
-
|
428
|
+
the_argument.ref_name
|
327
429
|
else
|
328
|
-
if
|
329
|
-
|
430
|
+
if the_argument.to_s.rid? # a string with "#ab:cd"
|
431
|
+
the_argument
|
330
432
|
else # a database-class-name
|
331
|
-
|
433
|
+
the_argument.to_s
|
332
434
|
end
|
333
435
|
end
|
334
|
-
|
335
|
-
|
336
|
-
|
436
|
+
else
|
437
|
+
raise "cannot complete until a target is specified"
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
441
|
+
=begin
|
442
|
+
from can either be a Databaseclass to operate on or a Subquery providing data to query further
|
443
|
+
=end
|
444
|
+
def from arg = nil
|
445
|
+
if arg.present?
|
446
|
+
@q[:database] = arg
|
447
|
+
self # return query-object
|
448
|
+
elsif @q[:database].present? # read from
|
449
|
+
"from #{ target }"
|
450
|
+
end
|
337
451
|
end
|
338
|
-
|
339
|
-
|
452
|
+
|
453
|
+
|
454
|
+
def order value = nil
|
455
|
+
if value.present?
|
456
|
+
@q[:order] << value
|
457
|
+
self
|
458
|
+
elsif @q[:order].present?
|
459
|
+
|
460
|
+
"order by " << @q[:order].compact.flatten.map do |o|
|
461
|
+
case o
|
462
|
+
when String, Symbol, Array
|
463
|
+
o.to_s
|
464
|
+
else
|
465
|
+
o.map{|x,y| "#{x} #{y}"}.join(" ")
|
466
|
+
end # case
|
467
|
+
end.join(', ')
|
468
|
+
else
|
469
|
+
''
|
470
|
+
end # unless
|
471
|
+
end # def
|
472
|
+
|
340
473
|
|
341
474
|
def database_class # :nodoc:
|
342
|
-
|
343
|
-
@database
|
344
|
-
elsif @from.is_a? OrientQuery
|
345
|
-
@from.database_class
|
346
|
-
else
|
347
|
-
nil
|
348
|
-
end
|
475
|
+
@q[:database]
|
349
476
|
end
|
350
477
|
|
351
478
|
def database_class= arg # :nodoc:
|
352
|
-
@database = arg
|
353
|
-
if @from.is_a? OrientQuery
|
354
|
-
@from.database_class= arg
|
355
|
-
end
|
356
|
-
end
|
357
|
-
|
358
|
-
def where_s # :nodoc:
|
359
|
-
compose_where @where
|
479
|
+
@q[:database] = arg
|
360
480
|
end
|
361
481
|
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
when String
|
367
|
-
s
|
368
|
-
when Array
|
369
|
-
s.join(', ')
|
370
|
-
# when Hash ### is not recognized in jruby
|
371
|
-
else
|
372
|
-
s.map{|x,y| "$#{x} = (#{y})"}.join(', ')
|
373
|
-
end
|
374
|
-
end.join(', ')
|
375
|
-
end
|
376
|
-
end
|
482
|
+
def distinct d
|
483
|
+
@q[:projection] << "distinct " + generate_sql_list( d ){ ' as ' }
|
484
|
+
self
|
485
|
+
end
|
377
486
|
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
487
|
+
class << self
|
488
|
+
def mk_simple_setter *m
|
489
|
+
m.each do |def_m|
|
490
|
+
define_method( def_m ) do | value=nil |
|
491
|
+
if value.present?
|
492
|
+
@q[def_m] = value
|
493
|
+
self
|
494
|
+
elsif @q[def_m].present?
|
495
|
+
"#{def_m.to_s} #{generate_sql_list(@q[def_m]){' ,'}}"
|
496
|
+
end
|
497
|
+
end
|
385
498
|
end
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
499
|
+
end
|
500
|
+
def mk_std_setter *m
|
501
|
+
m.each do |def_m|
|
502
|
+
define_method( def_m ) do | value = nil |
|
503
|
+
if value.present?
|
504
|
+
@q[def_m] << case value
|
505
|
+
when String
|
506
|
+
value
|
507
|
+
when ::Hash
|
508
|
+
value.map{|k,v| "#{k} = #{v.to_or}"}.join(", ")
|
509
|
+
else
|
510
|
+
raise "Only String or Hash allowed in #{def_m} statement"
|
511
|
+
end
|
512
|
+
self
|
513
|
+
elsif @q[def_m].present?
|
514
|
+
"#{def_m.to_s} #{@q[def_m].join(',')}"
|
515
|
+
end # branch
|
516
|
+
end # def_method
|
517
|
+
end # each
|
518
|
+
end # def
|
519
|
+
end # class << self
|
520
|
+
mk_simple_setter :limit, :skip, :unwind
|
521
|
+
mk_std_setter :set, :remove
|
522
|
+
|
523
|
+
def let value = nil
|
524
|
+
if value.present?
|
525
|
+
@q[:let] << value
|
526
|
+
self
|
527
|
+
elsif @q[:let].present?
|
528
|
+
"let " << @q[:let].map do |s|
|
529
|
+
case s
|
530
|
+
when String
|
531
|
+
s
|
532
|
+
when ::Hash
|
533
|
+
s.map do |x,y|
|
534
|
+
# if the symbol: value notation of Hash is used, add "$" to the key
|
535
|
+
x = "$#{x.to_s}" unless x.is_a?(String) && x[0] == "$"
|
536
|
+
"#{x} = #{ case y
|
537
|
+
when self.class
|
538
|
+
"(#{y.compose})"
|
539
|
+
else
|
540
|
+
y.to_orient
|
541
|
+
end }"
|
542
|
+
end
|
543
|
+
end
|
544
|
+
end.join(', ')
|
545
|
+
end
|
546
|
+
end
|
547
|
+
#
|
548
|
+
def projection value= nil # :nodoc:
|
549
|
+
if value.present?
|
550
|
+
@q[:projection] << value
|
551
|
+
self
|
552
|
+
elsif @q[:projection].present?
|
553
|
+
@q[:projection].compact.map do | s |
|
554
|
+
case s
|
555
|
+
when ::Array
|
556
|
+
s.join(', ')
|
557
|
+
when String, Symbol
|
558
|
+
s.to_s
|
559
|
+
when ::Hash
|
560
|
+
s.map{ |x,y| "#{x} as #{y}"}.join( ', ')
|
561
|
+
end
|
562
|
+
end.join( ', ' )
|
563
|
+
end
|
564
|
+
end
|
402
565
|
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
566
|
+
|
567
|
+
|
568
|
+
def group value = nil
|
569
|
+
if value.present?
|
570
|
+
@q[:group] << value
|
571
|
+
self
|
572
|
+
elsif @q[:group].present?
|
573
|
+
"group by #{@q[:group].join(', ')}"
|
574
|
+
end
|
407
575
|
end
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
576
|
+
|
577
|
+
alias order_by order
|
578
|
+
alias group_by group
|
579
|
+
|
580
|
+
def get_limit # :nodoc:
|
581
|
+
@q[:limit].nil? ? -1 : @q[:limit].to_i
|
412
582
|
end
|
413
583
|
|
414
584
|
def expand item
|
415
|
-
@projection =[ " expand ( #{item.to_s} )" ]
|
416
|
-
|
585
|
+
@q[:projection] =[ " expand ( #{item.to_s} )" ]
|
586
|
+
self
|
417
587
|
end
|
418
588
|
|
589
|
+
# connects by adding {in_or_out}('edgeClass')
|
590
|
+
def connect_with in_or_out, via: nil
|
591
|
+
argument = " #{in_or_out}(#{via.to_or if via.present?})"
|
592
|
+
end
|
593
|
+
# adds a connection
|
594
|
+
# in_or_out: :out ---> outE('edgeClass').in[where-condition]
|
595
|
+
# :in ---> inE('edgeClass').out[where-condition]
|
419
596
|
|
420
|
-
def nodes in_or_out = :out, via:
|
597
|
+
def nodes in_or_out = :out, via: nil, where: nil, expand: true
|
421
598
|
condition = where.present? ? "[ #{generate_sql_list(where)} ]" : ""
|
422
|
-
start = in_or_out
|
423
|
-
|
424
|
-
|
599
|
+
start = if in_or_out == :in
|
600
|
+
'inE'
|
601
|
+
elsif in_or_out == :out
|
602
|
+
'outE'
|
603
|
+
else
|
604
|
+
"both"
|
605
|
+
end
|
606
|
+
the_end = if in_or_out == :in
|
607
|
+
'.out'
|
608
|
+
elsif in_or_out == :out
|
609
|
+
'.in'
|
610
|
+
else
|
611
|
+
''
|
612
|
+
end
|
613
|
+
argument = " #{start}(#{[via].flatten.map(&:to_or).join(',') if via.present?})#{the_end}#{condition} "
|
425
614
|
|
426
615
|
if expand.present?
|
427
616
|
send :expand, argument
|
428
617
|
else
|
429
|
-
|
618
|
+
@q[:projection] << argument
|
430
619
|
end
|
431
|
-
|
620
|
+
self
|
432
621
|
end
|
433
622
|
|
434
|
-
def group_by g = nil
|
435
|
-
@group = "group by #{g.to_s}" if g.present?
|
436
|
-
# only a string is allowed
|
437
|
-
@group
|
438
|
-
end
|
439
|
-
|
440
|
-
def unwind u = nil
|
441
|
-
@unwind = "unwind #{u.to_s}" if u.present?
|
442
|
-
# only a string is allowed
|
443
|
-
@unwind
|
444
|
-
end
|
445
623
|
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
624
|
+
# returns nil if the query was not sucessfully executed
|
625
|
+
def execute(reduce: false)
|
626
|
+
#puts "Compose: #{compose}"
|
627
|
+
result = V.orientdb.execute{ compose }
|
628
|
+
return nil unless result.is_a?(::Array)
|
629
|
+
result = result.map{|x| yield x } if block_given?
|
630
|
+
return result.first if reduce && result.size == 1
|
631
|
+
## standard case: return Array
|
632
|
+
OrientSupport::Array.new( work_on: resolve_target, work_with: result.orient_flatten)
|
633
|
+
end
|
634
|
+
:protected
|
635
|
+
def resolve_target
|
636
|
+
if @q[:database].is_a? OrientSupport::OrientQuery
|
637
|
+
@q[:database].resolve_target
|
638
|
+
else
|
639
|
+
@q[:database]
|
640
|
+
end
|
641
|
+
end
|
450
642
|
|
451
|
-
|
452
|
-
|
453
|
-
# the [@order] is nessesary to enable query.order= "..." oder query.order= { a: :b }
|
454
|
-
"order by " << [@order].flatten.map do |o|
|
455
|
-
case o
|
456
|
-
when String, Symbol, Array
|
457
|
-
o.to_s
|
458
|
-
else
|
459
|
-
o.map{|x,y| "#{x} #{y}"}.join(" ")
|
460
|
-
end # case
|
461
|
-
end.join(', ')
|
462
|
-
else
|
463
|
-
''
|
464
|
-
end # unless
|
465
|
-
end # def
|
466
|
-
end # class
|
643
|
+
# end
|
644
|
+
end # class
|
467
645
|
|
468
646
|
|
469
647
|
end # module
|