active-orient 0.42 → 0.79

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.
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