active-orient 0.42 → 0.79
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +1 -0
- data/Gemfile +13 -5
- data/Guardfile +12 -4
- data/README.md +67 -280
- data/VERSION +1 -1
- data/active-orient.gemspec +6 -5
- data/bin/active-orient-0.6.gem +0 -0
- data/bin/active-orient-console +85 -0
- data/config/boot.rb +72 -1
- data/config/config.yml +10 -0
- data/config/connect.yml +9 -4
- data/examples/books.rb +92 -40
- data/examples/streets.rb +89 -85
- data/examples/test_commands.rb +97 -0
- data/examples/test_commands_2.rb +59 -0
- data/examples/test_commands_3.rb +55 -0
- data/examples/test_commands_4.rb +33 -0
- data/examples/time_graph.md +162 -0
- data/lib/active-orient.rb +75 -9
- data/lib/base.rb +238 -169
- data/lib/base_properties.rb +68 -60
- data/lib/class_utils.rb +226 -0
- data/lib/database_utils.rb +98 -0
- data/lib/init.rb +79 -0
- data/lib/java-api.rb +442 -0
- data/lib/jdbc.rb +211 -0
- data/lib/model/custom.rb +26 -0
- data/lib/model/edge.rb +70 -0
- data/lib/model/model.rb +134 -0
- data/lib/model/the_class.rb +607 -0
- data/lib/model/the_record.rb +266 -0
- data/lib/model/vertex.rb +236 -0
- data/lib/orientdb_private.rb +48 -0
- data/lib/other.rb +371 -0
- data/lib/railtie.rb +68 -0
- data/lib/rest/change.rb +147 -0
- data/lib/rest/create.rb +279 -0
- data/lib/rest/delete.rb +134 -0
- data/lib/rest/operations.rb +211 -0
- data/lib/rest/read.rb +171 -0
- data/lib/rest/rest.rb +112 -0
- data/lib/rest_disabled.rb +24 -0
- data/lib/support/logging.rb +38 -0
- data/lib/support/orient.rb +196 -0
- data/lib/support/orientquery.rb +469 -0
- data/rails.md +154 -0
- data/rails/activeorient.rb +32 -0
- data/rails/config.yml +10 -0
- data/rails/connect.yml +17 -0
- metadata +65 -24
- data/active-orient-0.4.gem +0 -0
- data/active-orient-0.41.gem +0 -0
- data/lib/model.rb +0 -468
- data/lib/orient.rb +0 -98
- data/lib/query.rb +0 -88
- data/lib/rest.rb +0 -1059
- data/lib/support.rb +0 -372
- data/test.rb +0 -4
- 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
|