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.
- checksums.yaml +4 -4
- data/.graphs.txt.swp +0 -0
- data/Gemfile +2 -6
- data/README.md +29 -27
- data/VERSION +1 -1
- data/active-orient.gemspec +4 -3
- data/bin/active-orient-console +18 -6
- data/changelog.md +60 -0
- data/config/connect.yml +8 -8
- data/examples/books.rb +134 -97
- data/graphs.txt +70 -0
- data/lib/active-orient.rb +2 -0
- data/lib/base.rb +38 -17
- data/lib/base_properties.rb +15 -14
- data/lib/class_utils.rb +11 -50
- data/lib/database_utils.rb +23 -22
- data/lib/init.rb +4 -3
- data/lib/model/custom.rb +7 -4
- data/lib/model/e.rb +6 -0
- data/lib/model/edge.rb +74 -30
- data/lib/model/the_class.rb +181 -131
- data/lib/model/the_record.rb +115 -68
- data/lib/model/vertex.rb +261 -126
- data/lib/other.rb +93 -41
- data/lib/rest/change.rb +23 -20
- data/lib/rest/create.rb +71 -63
- data/lib/rest/delete.rb +80 -64
- data/lib/rest/operations.rb +79 -68
- data/lib/rest/read.rb +42 -24
- data/lib/rest/rest.rb +38 -30
- data/lib/support/conversions.rb +42 -0
- data/lib/support/default_formatter.rb +7 -0
- data/lib/support/errors.rb +41 -0
- data/lib/support/orient.rb +167 -58
- data/lib/support/orientquery.rb +526 -348
- data/lib/support/query.rb +92 -0
- metadata +34 -18
- data/examples/test_commands.rb +0 -97
- data/examples/test_commands_2.rb +0 -59
- data/examples/test_commands_3.rb +0 -55
- data/examples/test_commands_4.rb +0 -33
- data/examples/time_graph.md +0 -162
data/lib/model/the_record.rb
CHANGED
@@ -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.
|
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
|
57
|
+
Execute a Query using the current model-record as origin.
|
50
58
|
|
51
|
-
It sends the OrientSupport::OrientQuery
|
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
|
59
|
-
|
60
|
-
query.from
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
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
|
112
|
-
orientdb.delete_record
|
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
|
-
|
132
|
+
Convenient update of the dataset
|
122
133
|
|
123
|
-
|
134
|
+
A) Using PATCH
|
124
135
|
|
125
|
-
|
136
|
+
Previously changed attributes are saved to the database.
|
126
137
|
|
127
|
-
|
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
|
-
|
133
|
-
obj.update yesterdays_event: 35
|
134
|
-
is identical
|
135
|
-
=end
|
145
|
+
B) Manual Modus
|
136
146
|
|
137
|
-
|
138
|
-
|
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
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
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
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
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
|
-
|
201
|
+
end
|
202
|
+
end
|
159
203
|
|
160
204
|
# mocking active record
|
161
205
|
def update_attribute the_attribute, the_value # :nodoc:
|
162
|
-
update
|
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
|
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
|
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
|
data/lib/model/vertex.rb
CHANGED
@@ -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
|
8
|
-
|
9
|
-
|
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
|
20
|
-
|
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
|
-
|
27
|
+
|
28
|
+
The rid-cache is reset, too
|
23
29
|
=end
|
24
|
-
def self.delete where:
|
25
|
-
|
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
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
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
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
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
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
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
|
-
|
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
|
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/
|
194
|
+
The basic usage is to fetch all/ incoming/ outgoing edges
|
123
195
|
|
124
|
-
Model-Instance.edges :in
|
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
|
129
|
-
Model-Instance.edges /sector/
|
130
|
-
|
131
|
-
|
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
|
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
|
240
|
+
Human readable representation of Vertices
|
208
241
|
|
209
|
-
Format: < Classname
|
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}
|
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
|