active-orient 0.79 → 0.80

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