active-orient 0.42 → 0.79

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/Gemfile +13 -5
  4. data/Guardfile +12 -4
  5. data/README.md +67 -280
  6. data/VERSION +1 -1
  7. data/active-orient.gemspec +6 -5
  8. data/bin/active-orient-0.6.gem +0 -0
  9. data/bin/active-orient-console +85 -0
  10. data/config/boot.rb +72 -1
  11. data/config/config.yml +10 -0
  12. data/config/connect.yml +9 -4
  13. data/examples/books.rb +92 -40
  14. data/examples/streets.rb +89 -85
  15. data/examples/test_commands.rb +97 -0
  16. data/examples/test_commands_2.rb +59 -0
  17. data/examples/test_commands_3.rb +55 -0
  18. data/examples/test_commands_4.rb +33 -0
  19. data/examples/time_graph.md +162 -0
  20. data/lib/active-orient.rb +75 -9
  21. data/lib/base.rb +238 -169
  22. data/lib/base_properties.rb +68 -60
  23. data/lib/class_utils.rb +226 -0
  24. data/lib/database_utils.rb +98 -0
  25. data/lib/init.rb +79 -0
  26. data/lib/java-api.rb +442 -0
  27. data/lib/jdbc.rb +211 -0
  28. data/lib/model/custom.rb +26 -0
  29. data/lib/model/edge.rb +70 -0
  30. data/lib/model/model.rb +134 -0
  31. data/lib/model/the_class.rb +607 -0
  32. data/lib/model/the_record.rb +266 -0
  33. data/lib/model/vertex.rb +236 -0
  34. data/lib/orientdb_private.rb +48 -0
  35. data/lib/other.rb +371 -0
  36. data/lib/railtie.rb +68 -0
  37. data/lib/rest/change.rb +147 -0
  38. data/lib/rest/create.rb +279 -0
  39. data/lib/rest/delete.rb +134 -0
  40. data/lib/rest/operations.rb +211 -0
  41. data/lib/rest/read.rb +171 -0
  42. data/lib/rest/rest.rb +112 -0
  43. data/lib/rest_disabled.rb +24 -0
  44. data/lib/support/logging.rb +38 -0
  45. data/lib/support/orient.rb +196 -0
  46. data/lib/support/orientquery.rb +469 -0
  47. data/rails.md +154 -0
  48. data/rails/activeorient.rb +32 -0
  49. data/rails/config.yml +10 -0
  50. data/rails/connect.yml +17 -0
  51. metadata +65 -24
  52. data/active-orient-0.4.gem +0 -0
  53. data/active-orient-0.41.gem +0 -0
  54. data/lib/model.rb +0 -468
  55. data/lib/orient.rb +0 -98
  56. data/lib/query.rb +0 -88
  57. data/lib/rest.rb +0 -1059
  58. data/lib/support.rb +0 -372
  59. data/test.rb +0 -4
  60. data/usecase.md +0 -91
@@ -0,0 +1,24 @@
1
+ =begin #nodoc#
2
+ If properties are allocated on class-level, they can be preinitialized using
3
+ this method.
4
+ This is disabled for now, because it does not seem nessesary
5
+ =end
6
+
7
+ def preallocate_class_properties o_class # :nodoc:
8
+ p= get_class_properties( o_class )['properties']
9
+ unless p.nil? || p.blank?
10
+ predefined_attributes = p.map do | property |
11
+ [ property['name'] ,
12
+ case property['type']
13
+ when 'LINKMAP'
14
+ Array.new
15
+ when 'STRING'
16
+ ''
17
+ else
18
+ nil
19
+ end ]
20
+ end.to_h
21
+ else
22
+ {}
23
+ end
24
+ end
@@ -0,0 +1,38 @@
1
+ #require_relative 'default_formatter'
2
+ module OrientSupport
3
+ module Logging
4
+ def self.included(base)
5
+ base.extend ClassMethods
6
+ base.send :define_method, :logger do
7
+ base.logger
8
+ end
9
+ end
10
+
11
+ module ClassMethods
12
+ def logger
13
+ @logger
14
+ end
15
+
16
+ def logger=(logger)
17
+ @logger = logger
18
+ end
19
+
20
+ def configure_logger(log= nil)
21
+ if log
22
+ @logger = log
23
+ else
24
+ @logger = Logger.new(STDOUT)
25
+ @logger.level = Logger::INFO
26
+ @logger.formatter = DefaultFormatter
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ class DefaultFormatter < Logger::Formatter
33
+ def self.call(severity, time, program_name, msg)
34
+ "#{time.strftime("%d.%m.(%X)")}#{"%5s" % severity}->#{msg}\n"
35
+ end
36
+ end
37
+ end
38
+ # source: https://github.com/jondot/sneakers/blob/master/lib/sneakers/concerns/logging.rb
@@ -0,0 +1,196 @@
1
+ module OrientSupport
2
+
3
+ # This Module fences specialized Ruby objects
4
+
5
+ # The Array _knows_ its database-class. This enables database-transactions outside the scope
6
+ # of ActiveOrient
7
+ #
8
+ # The Database-Class is available through Array#record
9
+
10
+
11
+ class Array < Array
12
+ include OrientSupport::Support
13
+
14
+ =begin
15
+ During initialisation the model-instance to work on is stored in @orient.
16
+
17
+ The keyword_parameter »work_on« holds the record to work on.
18
+ The second argument holds the array to work with
19
+
20
+ If instead of a model-instance the model-class is provided, a new model-instance is created and returned
21
+ Its up to the caller to save the new instance in the database
22
+
23
+ Further a list of array-elements is expected, which are forwarded (as Array) to Array
24
+
25
+ Its used to initialize Objects comming from the database (i.e. /lib/base.rb)
26
+
27
+ elsif iv.is_a? Array
28
+ OrientSupport::Array.new( work_on: self, work_with: iv.from_orient){ key.to_sym }
29
+
30
+ =end
31
+
32
+ def initialize( work_on:, work_with: )
33
+ @orient = work_on.class == Class ? work_on.new : work_on
34
+ super work_with
35
+ begin
36
+ @name = @orient.attributes.key(self)
37
+ rescue TypeError => e # not defined
38
+ ActiveOrient::Base.logger.debug{ "--------------------Type Error ----------------------------------" }
39
+ ActiveOrient::Base.logger.debug("OrientSupport::Array"){ "Attributes #{@orient.attributes.inspect}" }
40
+ ActiveOrient::Base.logger.debug("OrientSupport::Array"){ e.inspect
41
+ ActiveOrient::Base.logger.debug{ "indicates a try to access a non existing array element" }}
42
+ nil
43
+ rescue NameError =>e
44
+ ActiveOrient::Base.logger.debug{ "--------------------Name Error ------------" }
45
+ ActiveOrient::Base.logger.debug ("OrientSupport::Array"){ e.inspect }
46
+ #ActiveOrient::Base.logger.error{ e.backtrace.map {|l| " #{l}\n"}.join }
47
+ ActiveOrient::Base.logger.debug{ "due to a bug in ActiveSupport DateTime Calculations" }
48
+ # we just ignore the error
49
+ end
50
+ @name = yield if @name.nil? && block_given?
51
+ end
52
+ def as_json o=nil
53
+ map{|x| x.rid? ? x.rid : x }
54
+ end
55
+
56
+ def record
57
+ @orient
58
+ end
59
+
60
+ def to_human
61
+ map &:to_human
62
+ end
63
+ # returns the modified array and is chainable
64
+ #
65
+ # i= V.get( '89:0')
66
+ # ii=i.zwoebelkuchen << 'z78' << 6 << [454, 787]
67
+ # => [7, 5, 6, "z78", 78, 45, "z78", 6, 454, 787]
68
+ =begin
69
+ Append the argument to the Array, changes the Array itself.
70
+
71
+ The change is immediately transmitted to the database.
72
+
73
+
74
+ =end
75
+ def append *arg
76
+
77
+ @orient.update { "#{@name.to_s} = #{@name} || #{arg.to_or} "}[@name]
78
+ end
79
+
80
+ alias << append
81
+
82
+ def remove *k
83
+ # todo combine queries in a transaction
84
+ puts "delete: #{@name} --< #{k.map(&:to_or).join( ' :: ' )}"
85
+ k.each{|item| @orient.remove( " #{@name} = #{item.to_or}")[@name] }
86
+ end
87
+
88
+
89
+
90
+ =begin
91
+ Updating of single items
92
+ =end
93
+
94
+ def []= key, value
95
+ super
96
+ @orient.update set: {@name => self} if @name.present?
97
+ end
98
+
99
+
100
+ ###
101
+ ## just works with Hashes as parameters
102
+ def where *item
103
+ where_string = item.map{|m| where_string = compose_where( m ) }.join(' and ')
104
+ subquery= OrientSupport::OrientQuery.new from: @orient, projection: "expand( #{@name})"
105
+ q= OrientSupport::OrientQuery.new from: subquery, where: item
106
+ @orient.query q.to_s
107
+
108
+ end
109
+
110
+ def method_missing *args
111
+
112
+ self.map{|x| x.send *args }
113
+ rescue NoMethodError => e
114
+ ActiveOrient::Base.logger.error("OrientSupport::Array"){ "MethodMissing -> Undefined method: #{args.first} -- Args: #{args[1..-1].inspect}"}
115
+ ActiveOrient::Base.logger.error {" The Message #{e.message}"}
116
+ ActiveOrient::Base.logger.error{ e.backtrace.map {|l| " #{l}\n"}.join }
117
+ end
118
+
119
+ end #Class
120
+
121
+
122
+
123
+
124
+ class Hash < Hash # WithIndifferentAccess
125
+ include OrientSupport::Support
126
+ def initialize modelinstance, args
127
+ super()
128
+ # puts "Hash.new args #{args}"
129
+ @orient = modelinstance
130
+ self.merge! args
131
+ @name = modelinstance.attributes.key(self)
132
+ @name = yield if @name.nil? && block_given?
133
+ # puts "@name #{@name}"
134
+ self
135
+ end
136
+
137
+
138
+ def []= k, v
139
+ @orient.update { "#{@name.to_s}.#{k.to_s} = #{v.to_or}" }
140
+ end
141
+
142
+ # Inserts the provided Hash to the (possibly emty) list-property and returns a hash
143
+ def append arg
144
+ # the argument is simply provided as JSON-parameter to »update«
145
+ # generated query: update {rrid} set { @name } = { arg.to_json } return after @this
146
+ # todo : consider returning a OrientSuport::Hash
147
+ @orient.update { "#{@name.to_s} = "+ arg.to_json }[@name]
148
+ end
149
+
150
+ alias << append
151
+
152
+ # removes a key-value entry from the hash.
153
+ #
154
+ # parameter: list of key's (duplicate values are removed)
155
+ def remove *k
156
+ # todo combine queries in a transaction
157
+ k.map{ |key| @orient.update( remove: true ) { "#{@name.to_s}.#{key} " } }.last
158
+ end
159
+ # def delete *key
160
+ #
161
+ # key.each do | k |
162
+ # o = OrientSupport::OrientQuery.new from: @orient,
163
+ # kind: 'update',
164
+ # set: "#{@name}.#{k.to_s}",
165
+ # return: "$current.#{@name}"
166
+ # @orient.db.execute{ o.to_s.gsub( 'set ', 'remove ' ) }.first.send( @name ) # extracts the modified array (from DB) from the result
167
+ # end
168
+ # @orient.reload!
169
+ # @orient.send @name # return value
170
+ # end
171
+
172
+ def delete_if &b
173
+ super &b
174
+ @orient.update set:{ @name => self}
175
+
176
+ end
177
+
178
+
179
+ end
180
+ end #Module
181
+
182
+ class Hash
183
+
184
+ def to_human
185
+ "{ " + self.map{ |k,v| [k.to_s,": ", v.to_orient].join }.join(', ') + " }"
186
+ end
187
+
188
+ def coerce arg
189
+ if arg.is_a? DateTime
190
+ nil
191
+ else
192
+ super
193
+
194
+ end
195
+ end
196
+ end
@@ -0,0 +1,469 @@
1
+ require 'active_support/inflector'
2
+ module OrientSupport
3
+ module Support
4
+
5
+ =begin
6
+ supports
7
+ where: 'string'
8
+ where: { property: 'value', property: value, ... }
9
+ where: ['string, { property: value, ... }, ... ]
10
+
11
+ Used by update and select
12
+
13
+ _Usecase:_
14
+ ORD.compose_where 'z=34', {u:6}
15
+ => "where z=34 and u = 6"
16
+ =end
17
+
18
+ #
19
+ def compose_where *arg , &b
20
+ arg = arg.flatten
21
+ return "" if arg.blank? || arg.size == 1 && arg.first.blank?
22
+ "where " + generate_sql_list( arg , &b)
23
+ end
24
+
25
+ =begin
26
+ designs a list of "Key = Value" pairs combined by "and" or the binding provided by the block
27
+ ORD.generate_sql_list where: 25 , upper: '65'
28
+ => "where = 25 and upper = '65'"
29
+ ORD.generate_sql_list( con_id: 25 , symbol: :G) { ',' }
30
+ => "con_id = 25 , symbol = 'G'"
31
+ =end
32
+ def generate_sql_list attributes = {}, &b
33
+ fill = block_given? ? yield : 'and'
34
+ a= case attributes
35
+ when ::Hash
36
+ attributes.map do |key, value|
37
+ case value
38
+ when ActiveOrient::Model
39
+ "#{key} = #{value.rrid}"
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} ")
54
+ when ::Array
55
+ attributes.map{|y| generate_sql_list y, &b }.join( " #{fill} " )
56
+ when String
57
+ attributes
58
+ end
59
+ end
60
+ end
61
+
62
+
63
+ class MatchConnection
64
+ attr_accessor :as
65
+ def initialize edge: nil, direction: :both, as: nil, count: 1
66
+ @edge = edge
67
+ @direction = direction # may be :both, :in, :out
68
+ @as = as
69
+ @count = count
70
+ end
71
+
72
+ def direction= dir
73
+ @direction = dir
74
+ end
75
+
76
+
77
+ def direction
78
+ fillup = @edge.present? ? @edge : ''
79
+ case @direction
80
+ when :both
81
+ " -#{fillup}- "
82
+ when :in
83
+ " <-#{fillup}- "
84
+ when :out
85
+ " -#{fillup}-> "
86
+ end
87
+
88
+ end
89
+
90
+ def compose
91
+ ministatement = @as.present? ? "{ as: #{@as} } " : ""
92
+ (1 .. @count).map{|x| direction }.join("{}") << ministatement
93
+
94
+ end
95
+
96
+ end
97
+
98
+ class MatchStatement
99
+ include Support
100
+ attr_accessor :as
101
+ attr_accessor :where
102
+ def initialize match_class=nil, **args
103
+ @misc = []
104
+ @where = []
105
+ @while = []
106
+ @maxdepth = 0
107
+ @as = nil
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
133
+
134
+ def match_alias
135
+ "as: #{@as }"
136
+ end
137
+ def where_s
138
+ compose_where( @where ).gsub( /where/, 'where:(' )<< ")" unless @where.blank?
139
+ end
140
+
141
+ def maxdepth=x
142
+ @maxdepth = x
143
+ end
144
+
145
+ def method_missing method, *arg, &b
146
+ @misc << method.to_s << generate_sql_list(arg)
147
+ end
148
+
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
+ end
158
+
159
+ def compose
160
+
161
+ '{'+ [ "class: #{@match_class}",
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
169
+
170
+ class OrientQuery
171
+ include Support
172
+
173
+
174
+ attr_accessor :where
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
216
+ end
217
+
218
+
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
+
229
+
230
+ def misc # :nodoc:
231
+ @misc.join(' ') unless @misc.empty?
232
+ # self.to_s # return compiled result
233
+ end
234
+
235
+ def subquery # :nodoc:
236
+ nil
237
+ end
238
+
239
+
240
+
241
+ =begin
242
+ (only if kind == :match): connect
243
+
244
+ Add a connection to the match-query
245
+
246
+ A Match-Query alwas has an Entry-Stratement and maybe other Statements.
247
+ They are connected via " -> " (outE), "<-" (inE) or "--" (both).
248
+
249
+ The connection method adds a connection to the statement-stack.
250
+
251
+ Parameters:
252
+ direction: :in, :out, :both
253
+ edge_class: to restrict the Query on a certain Edge-Class
254
+ count: To repeat the connection
255
+ as: Includes a micro-statement to finalize the Match-Query
256
+ as: defines a output-variablet, which is used later in the return-statement
257
+
258
+ The method returns the OrientSupport::MatchConnection object, which can be modified further.
259
+ It is compiled by calling compose
260
+ =end
261
+
262
+ def connect direction, edge_class: nil, count: 1, as: nil
263
+ direction= :both unless [ :in, :out].include? direction
264
+ match_statements << m = OrientSupport::MatchConnection.new( direction: direction, count: count, as: as)
265
+ m
266
+ end
267
+
268
+ =begin
269
+ (only if kind == :match): statement
270
+
271
+ A Match Query consists of a simple start-statement
272
+ ( classname and where-condition ), a connection followd by other Statement-connection-pairs.
273
+ It performs a sub-query starting at the given entry-point.
274
+
275
+ Statement adds a statement to the statement-stack.
276
+ Statement returns the created OrientSupport::MatchStatement-record for further modifications.
277
+ It is compiled by calling »compose«.
278
+
279
+ OrientSupport::OrientQuery collects any "as"-directive for inclusion in the return-statement
280
+
281
+ Parameter (all optional)
282
+ Class: classname, :where: {}, while: {}, as: string, maxdepth: >0 ,
283
+
284
+ =end
285
+ def statement match_class= nil, **args
286
+ match_statements << s = OrientSupport::MatchStatement.new( mattch_class, args )
287
+ s
288
+ end
289
+ =begin
290
+ Output the compiled query
291
+ Parameter: destination (rest, batch )
292
+ If the query is submitted via the REST-Interface (as get-command), the limit parameter is extracted.
293
+ =end
294
+
295
+ def compose(destination: :batch)
296
+ if @kind == :match
297
+ unless @match_statements.empty?
298
+ match_query = @kind.to_s.upcase + " "+ @match_statements[0].compose_simple
299
+ match_query << @match_statements[1..-1].map( &:compose ).join
300
+ match_query << " RETURN "<< (@match_statements.map( &:as ).compact | @aliases).join(', ')
301
+ 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
+ end
310
+ end
311
+ alias :to_s :compose
312
+
313
+ =begin
314
+ from can either be a Databaseclass to operate on or a Subquery providing data to query further
315
+ =end
316
+
317
+
318
+ def from arg = nil
319
+ if arg.present?
320
+ @database = case arg
321
+ when ActiveOrient::Model # a single record
322
+ arg.rrid
323
+ when OrientQuery # result of a query
324
+ ' ( '+ arg.to_s + ' ) '
325
+ when Class
326
+ arg.ref_name
327
+ else
328
+ if arg.to_s.rid? # a string with "#ab:cd"
329
+ arg
330
+ else # a database-class-name
331
+ arg.to_s
332
+ end
333
+ end
334
+ compose # return the complete query
335
+ else # read from
336
+ "from #{@database}" unless @database.nil?
337
+ end
338
+ end
339
+ alias :from= :from
340
+
341
+ def database_class # :nodoc:
342
+ if @database.present?
343
+ @database
344
+ elsif @from.is_a? OrientQuery
345
+ @from.database_class
346
+ else
347
+ nil
348
+ end
349
+ end
350
+
351
+ def database_class= arg # :nodoc:
352
+ @database = arg if @database.present?
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
360
+ end
361
+
362
+ def let_s # :nodoc:
363
+ unless @let.empty?
364
+ "let " << @let.map do |s|
365
+ case s
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
377
+
378
+ def distinct d
379
+ @projection << case d
380
+ when String, Symbol
381
+ "distinct #{d.to_s} "
382
+ else
383
+ dd= d.to_a.flatten
384
+ "distinct #{dd.first.to_s} as #{dd.last}"
385
+ end
386
+ compose
387
+ end
388
+ alias :distinct= :distinct
389
+
390
+ def projection_s # :nodoc:
391
+ @projection.map do | s |
392
+ case s
393
+ when Array
394
+ s.join(', ')
395
+ when String, Symbol
396
+ s.to_s
397
+ else
398
+ s.map{ |x,y| "#{x} as #{y}"}.join( ', ')
399
+ end
400
+ end.join( ', ' )
401
+ end
402
+
403
+ def limit l=nil
404
+ @limit = "limit #{l.to_s}" if l.present?
405
+ # only a string is allowed
406
+ @limit
407
+ end
408
+ alias :limit= :limit
409
+
410
+ def get_limit # :nodoc:
411
+ @limit.nil? ? -1 : @limit.split(' ').last.to_i
412
+ end
413
+
414
+ def expand item
415
+ @projection =[ " expand ( #{item.to_s} )" ]
416
+ compose
417
+ end
418
+
419
+
420
+ def nodes in_or_out = :out, via: nil, where: nil, expand: true
421
+ condition = where.present? ? "[ #{generate_sql_list(where)} ]" : ""
422
+ start = in_or_out
423
+ the_end = in_or_out == :in ? :out : :in
424
+ argument = " #{start}E(#{via.to_or if via.present?}).#{the_end}#{condition} "
425
+
426
+ if expand.present?
427
+ send :expand, argument
428
+ else
429
+ @projection << argument
430
+ end
431
+ compose
432
+ end
433
+
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
+
446
+ def skip n = nil
447
+ @skip = n if n.present?
448
+ "skip #{@skip}" if @skip.present?
449
+ end
450
+
451
+ def order_s # :nodoc:
452
+ unless @order.empty?
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
467
+
468
+
469
+ end # module