active-orient 0.79 → 0.80

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,9 @@
1
1
  module ModelRecord
2
2
  ############### RECORD FUNCTIONS ###############
3
-
3
+
4
+ def to_s
5
+ to_human
6
+ end
4
7
  ############# GET #############
5
8
 
6
9
  def from_orient # :nodoc:
@@ -16,7 +19,7 @@ module ModelRecord
16
19
  flag whether a property exists on the Record-level
17
20
  =end
18
21
  def has_property? property
19
- attributes.keys.include? property.to_s
22
+ attributes.keys.include? property.to_sym
20
23
  end
21
24
 
22
25
  def properties
@@ -45,29 +48,38 @@ The extended representation of RID (format: *#00:00* )
45
48
  def to_or
46
49
  rid.rid? ? rrid : "{ #{embedded} }"
47
50
  end
51
+
52
+ # returns a OrientSupport::OrientQuery
53
+ def query **args
54
+ OrientSupport::OrientQuery.new( **{ from: self}.merge(args))
55
+ end
48
56
  =begin
49
- Query uses the current model-record as origin of the query.
57
+ Execute a Query using the current model-record as origin.
50
58
 
51
- It sends the OrientSupport::OrientQuery directly to the database and returns an
59
+ It sends the OrientSupport::OrientQuery to the database and returns an
52
60
  ActiveOrient::Model-Object or an Array of Model-Objects as result.
53
61
 
54
- *Usage:* Query the Database by traversing through edges and vertices starting at a known location
62
+ *Usage:* Query the Database by traversing through links, edges and vertices starting at a known location
55
63
 
56
64
  =end
57
65
 
58
- def query query
59
-
60
- query.from = rrid if query.is_a? OrientSupport::OrientQuery
61
- result = orientdb.execute do
62
- query.to_s
63
- end
64
- if result.is_a? Array
65
- OrientSupport::Array.new work_on: self, work_with: result
66
- else
67
- result
68
- end # return value
69
- end
70
-
66
+ # def execute query, delete_cash: false
67
+ #
68
+ # query.from rrid if query.is_a?( OrientSupport::OrientQuery) && query.from.nil?
69
+ # ActiveOrient::Base.remove_rid( self ) if delete_cash
70
+ # result = orientdb.execute{ query.to_s }
71
+ # result = if block_given?
72
+ # result.is_a?(Array)? result.map{|x| yield x } : yield(result)
73
+ # else
74
+ # result
75
+ # end
76
+ # if result.is_a? Array
77
+ # OrientSupport::Array.new work_on: self, work_with: result.orient_flatten
78
+ # else
79
+ # result
80
+ # end # return value
81
+ # end
82
+ #
71
83
  =begin
72
84
  Fires a »where-Query» to the database starting with the current model-record.
73
85
 
@@ -92,7 +104,7 @@ Returns the result-set, ie. a Query-Object which contains links to the addressed
92
104
  @metadata[:version]
93
105
  end
94
106
  end
95
-
107
+ private
96
108
  def version= version # :nodoc:
97
109
  @metadata[:version] = version
98
110
  end
@@ -101,6 +113,7 @@ Returns the result-set, ie. a Query-Object which contains links to the addressed
101
113
  @metadata[:version] += 1
102
114
  end
103
115
 
116
+ public
104
117
 
105
118
  ############# DELETE ###########
106
119
 
@@ -108,58 +121,89 @@ Returns the result-set, ie. a Query-Object which contains links to the addressed
108
121
  #
109
122
  # It is overloaded in Vertex and Edge.
110
123
 
111
- def remove
112
- orientdb.delete_record self
113
- ActiveOrient::Base.remove_rid self ##if is_edge? # removes the obj from the rid_store
124
+ def delete
125
+ orientdb.delete_record self
114
126
  end
115
127
 
116
- alias delete remove
117
128
 
118
129
  ########### UPDATE ############
119
130
 
120
131
  =begin
121
- Convient update of the dataset by calling sql-patch
132
+ Convenient update of the dataset
122
133
 
123
- Previously changed attributes are saved to the database.
134
+ A) Using PATCH
124
135
 
125
- Using the optional :set argument ad-hoc attributes can be defined
136
+ Previously changed attributes are saved to the database.
126
137
 
127
- obj = ActiveOrient::Model::Contracts.first
138
+ Using the optional »:set:« argument ad-hoc attributes can be defined
139
+ V.create_class :contracts
140
+ obj = Contracts.first
128
141
  obj.name = 'new_name'
129
142
  obj.update set: { yesterdays_event: 35 }
130
143
  updates both, the »name« and the »yesterdays_event«-properties
131
144
 
132
- _note:_ The keyword »set« is optional, thus
133
- obj.update yesterdays_event: 35
134
- is identical
135
- =end
145
+ B) Manual Modus
136
146
 
137
- def update set:{}, add: nil, to: nil, **args
138
- logger.progname = 'ActiveOrient::Model#Update'
147
+ Update accepts a Block. The contents are parsed to »set«. Manual conversion of ruby-objects
148
+ to the database-input format is necessary
139
149
 
140
- if block_given? # calling vs. a block is used internally
141
- # to remove an Item from lists and sets call update(remove: true){ query }
142
- set_or_remove = args[:remove].present? ? "remove" : "set"
143
- transfer_content from: query( "update #{rrid} #{set_or_remove} #{ yield } return after @this" )&.first
144
- else
145
- set.merge! args
146
- # set.merge updated_at: DateTime.now
150
+ i.e.
151
+ hct is an Array of ActiveOrient::Model-records.
152
+ then
153
+ obj.update { "positions = #{hct.to_or} " }
154
+ translates to
155
+ update #83:64 set positions = [#90:18, #91:18, #92:18] return after @this
156
+ and returns the modified record.
157
+
158
+ The manual modus accepts the keyword »remove«.
147
159
 
148
- if rid.rid?
149
- transfer_content from: db.update( self, set, version )
150
- # if the updated dataset changed, drop the changes made siently
151
- # self # return value
152
- else # new record
153
- @attributes.merge! set
154
- save
160
+ obj.update(remove: true) { "positions = #{hct.first.to_or} " }
161
+ translates to
162
+ update #83:64 remove positions = #90:18 return after @this
163
+
164
+ This can be achieved by
165
+ obj.positions
166
+
167
+
168
+ If the update process is not successful, nil is returned
169
+ =end
170
+
171
+ def update set: {}, remove: {}, **args
172
+ logger.progname = 'ActiveOrient::Model#Update'
173
+ # query( kind: update, )
174
+ if block_given? # calling vs. a block is used internally
175
+ # to remove an Item from lists and sets call update(remove: true){ query }
176
+ set_or_remove = args[:remove].present? ? "remove" : "set"
177
+ #transfer_content from:
178
+ updated_record = db.execute{ "update #{rrid} #{ yield } return after $current" } &.first
179
+ transfer_content from: updated_record if updated_record.present?
180
+ else
181
+ set = if remove.present?
182
+ { remove: remove.merge!( args) }
183
+ elsif set.present?
184
+ set.merge!( args)
185
+ else
186
+ args
187
+ end
188
+ # set.merge updated_at: DateTime.now
189
+ if rid.rid?
190
+ q= query.kind(:update)
191
+ if remove.present?
192
+ q.remove(remove)
193
+ else
194
+ q.set(set)
155
195
  end
196
+ transfer_content from: q.execute(reduce: true){ |y| y[:$current].reload! }
197
+ else # new record
198
+ self.attributes.merge! set
199
+ save
156
200
  end
157
-
158
- end
201
+ end
202
+ end
159
203
 
160
204
  # mocking active record
161
205
  def update_attribute the_attribute, the_value # :nodoc:
162
- update { " #{the_attribute} = #{the_value.to_or} " }
206
+ update the_attribute => the_value.to_or
163
207
  end
164
208
 
165
209
  def update_attributes **args # :nodoc:
@@ -196,29 +240,14 @@ Saves the record by calling update or creating the record
196
240
  end
197
241
 
198
242
 
199
- def transfer_content from:
200
- # »from« can be either
201
- # a model record (in case of create-record, get_record) or
202
- # a hash containing {"@type"=>"d", "@rid"=>"#xx:yy", "@version"=>n, "@class"=>'a_classname'}
203
- # and a list of updated properties (in case of db.update). Then update the version field and the
204
- # attributes.
205
- if from.is_a? ActiveOrient::Model
206
- @metadata = from.metadata
207
- @attributes = from.attributes
208
- else
209
- self.version = from['@version']
210
- # throw from["@..."] away and convert keys to symbols, merge that into attributes
211
- @attributes.merge! Hash[ from.delete_if{|k,_| k =~ /^@/}.map{|k,v| [k.to_sym, v.from_orient]}]
212
- end
213
- end
214
243
  ########## CHECK PROPERTY ########
215
244
 
216
245
  =begin
217
246
  An Edge is defined
218
- * when inherented from the superclass »E» (formal definition)
247
+ * when inherent from the superclass »E» (formal definition)
219
248
  * if it has an in- and an out property
220
249
 
221
- Actually we just check the second term as we trust the constuctor to work properly
250
+ Actually we just check the second term as we trust the constructor to work properly
222
251
  =end
223
252
 
224
253
  def is_edge? # :nodoc:
@@ -238,6 +267,7 @@ Example:
238
267
  a.test # <--- attribute: 'test' --> fetch attributes[:test]
239
268
 
240
269
  Assignments are performed only in ruby-space.
270
+
241
271
  Automatic database-updates are deactivated for now
242
272
  =end
243
273
  def method_missing *args
@@ -262,5 +292,22 @@ Automatic database-updates are deactivated for now
262
292
  end
263
293
  #end
264
294
 
265
-
295
+ #protected
296
+ def transfer_content from:
297
+ # »from« can be either
298
+ # a model record (in case of create-record, get_record) or
299
+ # a hash containing {"@type"=>"d", "@rid"=>"#xx:yy", "@version"=>n, "@class"=>'a_classname'}
300
+ # and a list of updated properties (in case of db.update). Then update the version field and the
301
+ # attributes.
302
+ return nil if from.nil?
303
+ if from.is_a? ActiveOrient::Model
304
+ @metadata = from.metadata
305
+ self.attributes = from.attributes
306
+ else
307
+ self.version = from['@version']
308
+ # throw away from["@..."] and convert keys to symbols, finally merge to attributes
309
+ @attributes.merge! Hash[ from.delete_if{|k,_| k =~ /^@/}.map{|k,v| [k.to_sym, v.from_orient]}]
310
+ end
311
+ self # return the modified object
312
+ end
266
313
  end
@@ -3,106 +3,178 @@ class V < ActiveOrient::Model
3
3
 
4
4
  =begin
5
5
  specialized creation of vertices, overloads model#create
6
+
7
+ Vertex.create set: { a: 1, b: "2", c: :r }
8
+
9
+ or
10
+
11
+ Vertex.create a: 1, b: "2", c: :r
12
+
13
+ If a record cannot be created, because an index inhibits it, the original record is
14
+ silently loaded instead.
15
+ To avoid this behavior, call create_record and specify »silence: false«
16
+
6
17
  =end
7
- def self.create( **keyword_arguments )
8
- new_vert = db.create_vertex self, attributes: keyword_arguments
9
- new_vert = new_vert.pop if new_vert.is_a?( Array) && new_vert.size == 1
10
- if new_vert.nil?
11
- logger.error('Vertex'){ "Table #{ref_name} ->> create failed: #{keyword_arguments.inspect}" }
12
- elsif block_given?
13
- yield new_vert
14
- else
15
- new_vert # returns the created vertex (or an array of created vertices)
16
- end
18
+ def self.create set: {}, **attributes
19
+ db.create_record self, attributes: set.merge(attributes)
20
+ # query.kind(:create).set( set.merge(attributes) ).execute(reduce: true)
17
21
  end
18
22
  =begin
19
- Vertex#delete fires a "delete vertex" command to the database.
20
- The where statement can be empty ( "" or {}"), then all vertices are removed
23
+ Vertex.delete fires a "delete vertex" command to the database.
24
+
25
+ To remove all records of a class, use »all: true« as argument
21
26
 
22
- The rid-cache is reseted, too
27
+
28
+ The rid-cache is reset, too
23
29
  =end
24
- def self.delete where: ""
25
- db.execute { "delete vertex #{ref_name} #{db.compose_where(where)}" }
30
+ def self.delete where: {} , **args
31
+ if args[:all] == true
32
+ where = {}
33
+ else
34
+ where.merge!(args) if where.is_a?(Hash)
35
+ return 0 if where.empty?
36
+ end
37
+ # query returns [{count => n }]
38
+ count= db.execute { "delete vertex #{ref_name} #{db.compose_where(where)}" }.first[:count] rescue 0
26
39
  reset_rid_store
40
+ count # return count of affected records
27
41
  end
28
42
 
29
- #Present Classes (Hierarchy)
30
- #---
31
- #- - E
32
- # - - - e1
33
- # - - e2
34
- # - e3
35
- #- - V
36
- # - - - v1
37
- # - - v2
38
43
 
39
- #v.to_human
40
- # => "<V2[#36:0]: in: {E2=>1}, node : 4>"
41
- #
42
- # v.detect_edges( :in, 2).to_human
43
- # => ["<E2: in : #<V2:0x0000000002e66228>, out : #<V1:0x0000000002ed0060>>"]
44
- # v.detect_edges( :in, E1).to_human
45
- # => ["<E2: in : #<V2:0x0000000002e66228>, out : #<V1:0x0000000002ed0060>>"]
46
- # v.detect_edges( :in, /e/).to_human
47
- # => ["<E2: in : #<V2:0x0000000002e66228>, out : #<V1:0x0000000002ed0060>>"]
48
- #
49
- def detect_edges kind = :in, edge_name = nil # :nodoc:
50
- ## returns a list of inherented classes
51
- get_superclass = ->(e) do
52
- n = orientdb.get_db_superclass(e)
53
- n =='E' ? e : e + ',' + get_superclass[n]
54
- end
55
- if edge_name.nil?
56
- edges(kind).map &:expand
44
+ =begin
45
+ Creates a new Match-Statement
46
+ =end
47
+ def self.match **args
48
+ OrientSupport::MatchStatement.new self, **args
49
+ end
50
+
51
+ =begin
52
+ Performs a Where-Query on the vertex-class
53
+
54
+ The where-cause narrows the sample to certain records.
55
+
56
+ They are returned as OrientSupport::Array
57
+
58
+ Internally a match-query is fired.
59
+
60
+ To fire a »select from class where« query, use »Class.custom_where«.
61
+ =end
62
+
63
+
64
+ def self.where *attributes
65
+ query_database( match(where: attributes).compile ) { | record | record[classname.pluralize.to_sym] }
66
+ end
67
+
68
+ =begin
69
+ List edges
70
+
71
+ 1. call without any parameter: list all edges present
72
+ 2. call with :in or :out : list any incoming or outgoing edges
73
+ 3. call with /regexp/, Class, symbol or string: restrict to this edges, including inheritence
74
+ If a pattern, symbol string or class is provided, the default is to list outgoing edges
75
+
76
+ :call-seq:
77
+ edges in_or_out, pattern
78
+ =end
79
+ def edges *args
80
+ if args.empty?
81
+ detect_edges :both
82
+ else
83
+ kind = [:in, :out, :both, :all].detect{|x| args.include? x }
84
+ if kind.present?
85
+ args = args - [ kind ]
57
86
  else
58
- e_name = if edge_name.is_a?(Regexp)
59
- edge_name
60
- else
61
- Regexp.new case edge_name
62
- when Class
63
- edge_name.ref_name
64
- when String
65
- edge_name
66
- when Symbol
67
- edge_name.to_s
68
- when Numeric
69
- edge_name.to_i.to_s
70
- end
71
- end
72
- the_edges = @metadata[:edges][kind].find_all{|y| get_superclass[y].split(',').detect{|x| x =~ e_name } }
73
-
74
- the_edges.map do | the_edge|
75
- candidate= attributes["#{kind.to_s}_#{the_edge}".to_sym]
76
- candidate.present? ? candidate.map( &:expand ).first : nil
77
- end
78
- end
79
- end
87
+ kind = :both
88
+ end
89
+ detect_edges kind, args.first
90
+
91
+ end
92
+ end
80
93
 
81
94
  # Lists all connected Vertices
95
+ # ( returns a OrientSupport::Array )
82
96
  #
83
97
  # The Edge-classes can be specified via Classname or a regular expression.
84
98
  #
85
- # If a regular expression is used, the database-names are searched.
99
+ # If a regular expression is used, the database-names are searched and inheritance is supported.
100
+ #
86
101
  def nodes in_or_out = :out, via: nil, where: nil, expand: false
87
- if via.present?
88
- edges = detect_edges( in_or_out, via )
89
- detected_nodes = edges.map do |e|
90
- q = OrientSupport::OrientQuery.new
91
- q.nodes in_or_out, via: e.class, where: where, expand: expand
92
- query( q )
93
- end.first
94
- end
102
+ edges = detect_edges( in_or_out, via, expand: false )
103
+ return [] if edges.empty?
104
+ q = query # q.to_s => "select from #0x:0x "
105
+ edges = nil if via.nil?
106
+ q.nodes in_or_out, via: edges , where: where, expand: expand
107
+ detected_nodes= q.execute{| record | record.is_a?(Hash)? record.values.first : record }
95
108
  end
96
109
 
97
110
 
98
-
111
+ # Returns a collection of all vertices passed during the traversal
112
+ #
113
+ # Includes the start_vertex (start_at =0 by default)
114
+ #
115
+ # If the vector should not include the start_vertex, call with `start_at:1` and increase the depth by 1
116
+ #
117
+ # fires a query
118
+ #
119
+ # select from ( traverse outE('}#{via}').in from #{vertex} while $depth < #{depth} )
120
+ # where $depth >= #{start_at}
121
+ #
122
+ # If » excecute: false « is specified, the traverse-statement is returned (as Orient-Query object)
123
+ def traverse in_or_out = :out, via: nil, depth: 1, execute: true, start_at: 0, where: nil
124
+
125
+ edges = detect_edges( in_or_out, via, expand: false)
126
+ the_query = query kind: 'traverse'
127
+ the_query.where where if where.present?
128
+ the_query.while "$depth < #{depth} " unless depth <=0
129
+ edges.each{ |ec| the_query.nodes in_or_out, via: ec, expand: false }
130
+ outer_query = OrientSupport::OrientQuery.new from: the_query, where: "$depth >= #{start_at}"
131
+ if execute
132
+ outer_query.execute
133
+ else
134
+ # the_query.from self # complete the query by assigning self
135
+ the_query # returns the OrientQuery -traverse object
136
+ end
137
+ end
138
+
139
+
140
+
141
+ =begin
142
+ Assigns another Vertex via an EdgeClass. If specified, puts attributes on the edge.
143
+
144
+ Wrapper for
145
+ Edge.create in: self, out: a_vertex, attributes: { some_attributes on the edge }
146
+
147
+ returns the assigned vertex, thus enabling to chain vertices through
148
+
149
+ Vertex.assign() via: E , vertex: VertexClass.create()).assign( via: E, ... )
150
+ or
151
+ (1..100).each{|n| vertex = vertex.assign(via: E2, vertex: V2.create(item: n))}
152
+ =end
153
+
154
+ def assign vertex: , via: E , attributes: {}
155
+
156
+ via.create from: self, to: vertex, set: attributes
157
+
158
+ vertex
159
+ end
160
+
161
+ ## Optimisation (not implemented jet)
162
+ #
163
+ # "LET $a = CREATE VERTEX VTest SET name = 'John';" +
164
+ # "CREATE EDGE ETest FROM :ParentRID TO $a;" +
165
+ # "RETURN $a;", params)
166
+
167
+
168
+
169
+
99
170
  =begin
100
171
  »in« and »out« provide the main access to edges.
101
- »in» is a reserved keyword. Therfore its only an alias to `in_e`.
172
+
173
+ »in» is a reserved keyword. Therefor its only an alias to `in_e`.
102
174
 
103
175
  If called without a parameter, all connected edges are retrieved.
104
176
 
105
- If called with a string, symbol or class, the edge-class is resolved and even inherented
177
+ If called with a string, symbol or class, the edge-class is resolved and even inherent
106
178
  edges are retrieved.
107
179
 
108
180
  =end
@@ -119,60 +191,21 @@ edges are retrieved.
119
191
  =begin
120
192
  Retrieves connected edges
121
193
 
122
- The basic usage is to fetch all/ incomming/ outgoing edges
194
+ The basic usage is to fetch all/ incoming/ outgoing edges
123
195
 
124
- Model-Instance.edges :in # :out | :all
196
+ Model-Instance.edges :in :out | :both, :all
125
197
 
126
198
  One can filter specific edges by providing parts of the edge-name
127
199
 
128
- Model-Instance.edges 'in_sector'
129
- Model-Instance.edges /sector/
130
-
131
- The method returns an array of rid's.
132
-
133
- Example:
134
-
135
- Industry.first.attributes.keys
136
- => ["in_sector_classification", "k", "name", "created_at", "updated_at"] # edge--> in ...
200
+ Model-Instance.edges /sector/, :in
201
+ Model-Instance.edges :out, /sector/
202
+ Model-Instance.edges /sector/
203
+ Model-Instance.edges :in
137
204
 
138
- Industry.first.edges :out
139
- => []
140
205
 
141
- Industry.first.edges :in
142
- => ["#61:0", "#61:9", "#61:21", "#61:33", "#61:39", "#61:93", "#61:120", "#61:150", "#61:240", "#61:252", "#61:264", "#61:279", "#61:303", "#61:339" ...]
143
-
144
206
 
207
+ The method returns an array of expands edges.
145
208
 
146
- To fetch the associated records use the ActiveOrient::Model.autoload_object method
147
-
148
- ActiveOrient::Model.autoload_object Industry.first.edges( :in).first
149
- # or
150
- Industry.autoload_object Industry.first.edges( /sector/ ).first
151
- => #<SectorClassification:0x00000002daad20 @metadata={"type"=>"d", "class"=>"sector_classification", "version"=>1, "fieldTypes"=>"out=x,in=x", "cluster"=>61, "record"=>0},(...)
152
-
153
- =end
154
-
155
- def edges kind=:all # :all, :in, :out
156
- expression = case kind
157
- when :all
158
- /^in|^out/
159
- when :in
160
- /^in/
161
- when :out
162
- /^out/
163
- when String
164
- /#{kind}/
165
- when Regexp
166
- kind
167
- else
168
- return []
169
- end
170
-
171
- edges = attributes.keys.find_all{ |x| x =~ expression }
172
- edges.map{|x| attributes[x]}.flatten
173
- end
174
-
175
- =begin
176
209
  »in_edges« and »out_edges« are shortcuts to »edges :in« and »edges :out«
177
210
 
178
211
  Its easy to expand the result:
@@ -191,7 +224,7 @@ returns all edges. The parameter (:out) is not recognized, because out is alread
191
224
 
192
225
  this
193
226
  tg.out( :ohlc).first.out.edges( :out)
194
- is a walkaround, but using in_- and out_edges is more elegant.
227
+ is a workaround, but using in_- and out_edges is more elegant.
195
228
  =end
196
229
  def in_edges
197
230
  edges :in
@@ -204,9 +237,9 @@ is a walkaround, but using in_- and out_edges is more elegant.
204
237
  # db.delete_vertex self
205
238
  # end
206
239
  =begin
207
- Human readable represantation of Vertices
240
+ Human readable representation of Vertices
208
241
 
209
- Format: < Classname : Edges, Attributes >
242
+ Format: < Classname: Edges, Attributes >
210
243
  =end
211
244
  def to_human
212
245
  count_and_display_classes = ->(array){array.map(&:class)&.group_by(&:itself)&.transform_values(&:count)}
@@ -223,7 +256,7 @@ Format: < Classname : Edges, Attributes >
223
256
  "<#{self.class.to_s.demodulize}[#{rid}]: " + in_and_out + content_attributes.map do |attr, value|
224
257
  v= case value
225
258
  when ActiveOrient::Model
226
- "< #{self.class.to_s.demodulize} : #{value.rid} >"
259
+ "< #{self.class.to_s.demodulize}: #{value.rid} >"
227
260
  when OrientSupport::Array
228
261
  value.to_s
229
262
  # value.rrid #.to_human #.map(&:to_human).join("::")
@@ -233,4 +266,106 @@ Format: < Classname : Edges, Attributes >
233
266
  "%s : %s" % [ attr, v] unless v.nil?
234
267
  end.compact.sort.join(', ') + ">".gsub('"' , ' ')
235
268
  end
269
+
270
+
271
+
272
+
273
+
274
+ #protected
275
+ #Present Classes (Hierarchy)
276
+ #---
277
+ #- - E
278
+ # - - - e1
279
+ # - - e2
280
+ # - e3
281
+ #- - V
282
+ # - - - v1
283
+ # - - v2
284
+
285
+ # v.to_human
286
+ # => "<V2[#36:0]: in: {E2=>1}, node : 4>"
287
+ #
288
+ # v.detect_edges( :in, 2).to_human
289
+ # => ["<E2: in : #<V2:0x0000000002e66228>, out : #<V1:0x0000000002ed0060>>"]
290
+ # v.detect_edges( :in, E1).to_human
291
+ # => ["<E2: in : #<V2:0x0000000002e66228>, out : #<V1:0x0000000002ed0060>>"]
292
+ # v.detect_edges( :in, /e/).to_human
293
+ # => ["<E2: in : #<V2:0x0000000002e66228>, out : #<V1:0x0000000002ed0060>>"]
294
+ #
295
+ #
296
+ # returns a OrientSupport::Array
297
+ def detect_edges kind = :in, edge_name = nil, expand: true #:nodoc:
298
+ ## returns a list of inherent DD classes
299
+ get_superclass = ->(e) do
300
+ if [nil,"", "e", "E", E, :e, :E ].include?(e)
301
+ "E"
302
+ else
303
+ n = orientdb.get_db_superclass(e)
304
+ n =='E' ? e : e + ',' + get_superclass[n]
305
+ end
306
+ end
307
+
308
+ expression = case kind
309
+ when :in
310
+ /^in_/
311
+ when :out
312
+ /^out_/
313
+ else
314
+ /^in_|^out_/
315
+ end
316
+
317
+ extract_database_class = ->(c){ y = c.to_s.gsub(expression, ''); y.empty? ? "E": y }
318
+ result, the_edges = [] # we have to declare result prior to its usage in the loop to make
319
+ # its content robust
320
+ attempt = 0
321
+ loop do
322
+ # get a set of available edge-names
323
+ # in_{abc} and out_{abc}
324
+ # with "out_" and "in_" as placeholder for E itself
325
+ # populate result in case no further action is required
326
+ result = the_edges = attributes.keys.find_all{ |x| x =~ expression }
327
+ # eager reloading
328
+ if result.empty? && attempt.zero?
329
+ reload!
330
+ attempt = 1
331
+ else
332
+ break
333
+ end
334
+ end
335
+
336
+ if edge_name.present?
337
+ # if a class is provided, match for the ref_name only
338
+ if edge_name.is_a?(Class)
339
+ result = [ the_edges.detect{ |x| edge_name.ref_name == extract_database_class[x] } ]
340
+ else
341
+ e_name = if edge_name.is_a?(Regexp)
342
+ edge_name
343
+ else
344
+ Regexp.new case edge_name
345
+ # when Class
346
+ # edge_name.ref_name
347
+ when String
348
+ edge_name
349
+ when Symbol
350
+ edge_name.to_s
351
+ when Numeric
352
+ edge_name.to_i.to_s
353
+ end
354
+ end
355
+ result = the_edges.find_all do |x|
356
+ get_superclass[extract_database_class[x] ].split(',').detect{|x| x =~ e_name }
357
+ end
358
+ end
359
+ end
360
+ # if expand = false , return the orientdb database name of the edges
361
+ # this is used by Vertex#nodes
362
+ # it avoids communications with the database prior to submitting the nodes-query
363
+ # if expand = true (default) load the edges instead
364
+ if expand
365
+ OrientSupport::Array.new work_on: self,
366
+ work_with: result.compact.map{|x| attributes[x]}.map(&:expand).orient_flatten
367
+ else
368
+ result.map{|x| extract_database_class[x] }
369
+ end
370
+ end
236
371
  end