active-orient 0.4 → 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 +5 -5
- data/.gitignore +1 -0
- data/.graphs.txt.swp +0 -0
- data/Gemfile +9 -5
- data/Guardfile +12 -4
- data/README.md +70 -281
- data/VERSION +1 -1
- data/active-orient.gemspec +9 -7
- data/bin/active-orient-0.6.gem +0 -0
- data/bin/active-orient-console +97 -0
- data/changelog.md +60 -0
- data/config/boot.rb +70 -17
- data/config/config.yml +10 -0
- data/config/connect.yml +11 -6
- data/examples/books.rb +154 -65
- data/examples/streets.rb +89 -85
- data/graphs.txt +70 -0
- data/lib/active-orient.rb +78 -6
- data/lib/base.rb +266 -168
- data/lib/base_properties.rb +76 -65
- data/lib/class_utils.rb +187 -0
- data/lib/database_utils.rb +99 -0
- data/lib/init.rb +80 -0
- data/lib/java-api.rb +442 -0
- data/lib/jdbc.rb +211 -0
- data/lib/model/custom.rb +29 -0
- data/lib/model/e.rb +6 -0
- data/lib/model/edge.rb +114 -0
- data/lib/model/model.rb +134 -0
- data/lib/model/the_class.rb +657 -0
- data/lib/model/the_record.rb +313 -0
- data/lib/model/vertex.rb +371 -0
- data/lib/orientdb_private.rb +48 -0
- data/lib/other.rb +423 -0
- data/lib/railtie.rb +68 -0
- data/lib/rest/change.rb +150 -0
- data/lib/rest/create.rb +287 -0
- data/lib/rest/delete.rb +150 -0
- data/lib/rest/operations.rb +222 -0
- data/lib/rest/read.rb +189 -0
- data/lib/rest/rest.rb +120 -0
- data/lib/rest_disabled.rb +24 -0
- 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/logging.rb +38 -0
- data/lib/support/orient.rb +305 -0
- data/lib/support/orientquery.rb +647 -0
- data/lib/support/query.rb +92 -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 +89 -30
- data/lib/model.rb +0 -461
- data/lib/orient.rb +0 -98
- data/lib/query.rb +0 -88
- data/lib/rest.rb +0 -1036
- data/lib/support.rb +0 -347
- data/test.rb +0 -4
- data/usecase.md +0 -91
@@ -0,0 +1,313 @@
|
|
1
|
+
module ModelRecord
|
2
|
+
############### RECORD FUNCTIONS ###############
|
3
|
+
|
4
|
+
def to_s
|
5
|
+
to_human
|
6
|
+
end
|
7
|
+
############# GET #############
|
8
|
+
|
9
|
+
def from_orient # :nodoc:
|
10
|
+
self
|
11
|
+
end
|
12
|
+
|
13
|
+
# Returns just the name of the Class
|
14
|
+
|
15
|
+
def self.classname # :nodoc:
|
16
|
+
self.class.to_s.split(':')[-1]
|
17
|
+
end
|
18
|
+
=begin
|
19
|
+
flag whether a property exists on the Record-level
|
20
|
+
=end
|
21
|
+
def has_property? property
|
22
|
+
attributes.keys.include? property.to_sym
|
23
|
+
end
|
24
|
+
|
25
|
+
def properties
|
26
|
+
{ "@type" => "d", "@class" => self.metadata[:class] }.merge attributes
|
27
|
+
end
|
28
|
+
|
29
|
+
#
|
30
|
+
# Obtain the RID of the Record (format: *00:00*)
|
31
|
+
#
|
32
|
+
|
33
|
+
def rid
|
34
|
+
begin
|
35
|
+
"#{@metadata[:cluster]}:#{@metadata[:record]}"
|
36
|
+
rescue
|
37
|
+
"0:0"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
=begin
|
41
|
+
The extended representation of RID (format: *#00:00* )
|
42
|
+
=end
|
43
|
+
def rrid
|
44
|
+
"#" + rid
|
45
|
+
end
|
46
|
+
alias to_orient rrid
|
47
|
+
|
48
|
+
def to_or
|
49
|
+
rid.rid? ? rrid : "{ #{embedded} }"
|
50
|
+
end
|
51
|
+
|
52
|
+
# returns a OrientSupport::OrientQuery
|
53
|
+
def query **args
|
54
|
+
OrientSupport::OrientQuery.new( **{ from: self}.merge(args))
|
55
|
+
end
|
56
|
+
=begin
|
57
|
+
Execute a Query using the current model-record as origin.
|
58
|
+
|
59
|
+
It sends the OrientSupport::OrientQuery to the database and returns an
|
60
|
+
ActiveOrient::Model-Object or an Array of Model-Objects as result.
|
61
|
+
|
62
|
+
*Usage:* Query the Database by traversing through links, edges and vertices starting at a known location
|
63
|
+
|
64
|
+
=end
|
65
|
+
|
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
|
+
#
|
83
|
+
=begin
|
84
|
+
Fires a »where-Query» to the database starting with the current model-record.
|
85
|
+
|
86
|
+
Attributes:
|
87
|
+
* a string ( obj.find "in().out().some_attribute >3" )
|
88
|
+
* a hash ( obj.find 'some_embedded_obj.name' => 'test' )
|
89
|
+
* an array
|
90
|
+
|
91
|
+
Returns the result-set, ie. a Query-Object which contains links to the addressed records.
|
92
|
+
|
93
|
+
=end
|
94
|
+
def find attributes = {}
|
95
|
+
q = OrientSupport::OrientQuery.new from: self, where: attributes
|
96
|
+
query q
|
97
|
+
end
|
98
|
+
|
99
|
+
# Get the version of the object
|
100
|
+
def version # :nodoc:
|
101
|
+
if document.present?
|
102
|
+
document.version
|
103
|
+
else
|
104
|
+
@metadata[:version]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
private
|
108
|
+
def version= version # :nodoc:
|
109
|
+
@metadata[:version] = version
|
110
|
+
end
|
111
|
+
|
112
|
+
def increment_version # :nodoc:
|
113
|
+
@metadata[:version] += 1
|
114
|
+
end
|
115
|
+
|
116
|
+
public
|
117
|
+
|
118
|
+
############# DELETE ###########
|
119
|
+
|
120
|
+
# Removes the Model-Instance from the database.
|
121
|
+
#
|
122
|
+
# It is overloaded in Vertex and Edge.
|
123
|
+
|
124
|
+
def delete
|
125
|
+
orientdb.delete_record self
|
126
|
+
end
|
127
|
+
|
128
|
+
|
129
|
+
########### UPDATE ############
|
130
|
+
|
131
|
+
=begin
|
132
|
+
Convenient update of the dataset
|
133
|
+
|
134
|
+
A) Using PATCH
|
135
|
+
|
136
|
+
Previously changed attributes are saved to the database.
|
137
|
+
|
138
|
+
Using the optional »:set:« argument ad-hoc attributes can be defined
|
139
|
+
V.create_class :contracts
|
140
|
+
obj = Contracts.first
|
141
|
+
obj.name = 'new_name'
|
142
|
+
obj.update set: { yesterdays_event: 35 }
|
143
|
+
updates both, the »name« and the »yesterdays_event«-properties
|
144
|
+
|
145
|
+
B) Manual Modus
|
146
|
+
|
147
|
+
Update accepts a Block. The contents are parsed to »set«. Manual conversion of ruby-objects
|
148
|
+
to the database-input format is necessary
|
149
|
+
|
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«.
|
159
|
+
|
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)
|
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
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
# mocking active record
|
205
|
+
def update_attribute the_attribute, the_value # :nodoc:
|
206
|
+
update the_attribute => the_value.to_or
|
207
|
+
end
|
208
|
+
|
209
|
+
def update_attributes **args # :nodoc:
|
210
|
+
update args
|
211
|
+
end
|
212
|
+
|
213
|
+
########## SAVE ############
|
214
|
+
|
215
|
+
=begin
|
216
|
+
Saves the record by calling update or creating the record
|
217
|
+
|
218
|
+
ORD.create_class :a
|
219
|
+
a = A.new
|
220
|
+
a.test = 'test'
|
221
|
+
a.save
|
222
|
+
|
223
|
+
a = A.first
|
224
|
+
a.test = 'test'
|
225
|
+
a.save
|
226
|
+
|
227
|
+
=end
|
228
|
+
def save
|
229
|
+
transfer_content from: if rid.rid?
|
230
|
+
db.update self, attributes, version
|
231
|
+
else
|
232
|
+
db.create_record self, attributes: attributes, cache: false
|
233
|
+
end
|
234
|
+
ActiveOrient::Base.store_rid self
|
235
|
+
end
|
236
|
+
|
237
|
+
def reload!
|
238
|
+
transfer_content from: db.get_record(rid)
|
239
|
+
self
|
240
|
+
end
|
241
|
+
|
242
|
+
|
243
|
+
########## CHECK PROPERTY ########
|
244
|
+
|
245
|
+
=begin
|
246
|
+
An Edge is defined
|
247
|
+
* when inherent from the superclass »E» (formal definition)
|
248
|
+
* if it has an in- and an out property
|
249
|
+
|
250
|
+
Actually we just check the second term as we trust the constructor to work properly
|
251
|
+
=end
|
252
|
+
|
253
|
+
def is_edge? # :nodoc:
|
254
|
+
attributes.keys.include?('in') && attributes.keys.include?('out')
|
255
|
+
end
|
256
|
+
|
257
|
+
=begin
|
258
|
+
How to handle other calls
|
259
|
+
|
260
|
+
* if attribute is specified, display it
|
261
|
+
* if attribute= is provided, assign to the known property or create a new one
|
262
|
+
|
263
|
+
Example:
|
264
|
+
ORD.create_class :a
|
265
|
+
a = A.new
|
266
|
+
a.test= 'test' # <--- attribute: 'test=', argument: 'test'
|
267
|
+
a.test # <--- attribute: 'test' --> fetch attributes[:test]
|
268
|
+
|
269
|
+
Assignments are performed only in ruby-space.
|
270
|
+
|
271
|
+
Automatic database-updates are deactivated for now
|
272
|
+
=end
|
273
|
+
def method_missing *args
|
274
|
+
# if the first entry of the parameter-array is a known attribute
|
275
|
+
# proceed with the assignment
|
276
|
+
if args.size == 1
|
277
|
+
attributes[args.first.to_sym] # return the attribute-value
|
278
|
+
elsif args[0][-1] == "="
|
279
|
+
if args.size == 2
|
280
|
+
# if rid.rid?
|
281
|
+
# update set:{ args[0][0..-2] => args.last }
|
282
|
+
# else
|
283
|
+
self.attributes[ args[0][0..-2] ] = args.last
|
284
|
+
# end
|
285
|
+
else
|
286
|
+
self.attributes[ args[0][0..-2] ] = args[1 .. -1]
|
287
|
+
# update set: {args[0][0..-2] => args[1 .. -1] } if rid.rid?
|
288
|
+
end
|
289
|
+
else
|
290
|
+
raise NameError, "Unknown method call #{args.first.to_s}", caller
|
291
|
+
end
|
292
|
+
end
|
293
|
+
#end
|
294
|
+
|
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
|
313
|
+
end
|
data/lib/model/vertex.rb
ADDED
@@ -0,0 +1,371 @@
|
|
1
|
+
class V < ActiveOrient::Model
|
2
|
+
## link to the library-class
|
3
|
+
|
4
|
+
=begin
|
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
|
+
|
17
|
+
=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)
|
21
|
+
end
|
22
|
+
=begin
|
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
|
26
|
+
|
27
|
+
|
28
|
+
The rid-cache is reset, too
|
29
|
+
=end
|
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
|
39
|
+
reset_rid_store
|
40
|
+
count # return count of affected records
|
41
|
+
end
|
42
|
+
|
43
|
+
|
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 ]
|
86
|
+
else
|
87
|
+
kind = :both
|
88
|
+
end
|
89
|
+
detect_edges kind, args.first
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Lists all connected Vertices
|
95
|
+
# ( returns a OrientSupport::Array )
|
96
|
+
#
|
97
|
+
# The Edge-classes can be specified via Classname or a regular expression.
|
98
|
+
#
|
99
|
+
# If a regular expression is used, the database-names are searched and inheritance is supported.
|
100
|
+
#
|
101
|
+
def nodes in_or_out = :out, via: nil, where: nil, expand: false
|
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 }
|
108
|
+
end
|
109
|
+
|
110
|
+
|
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
|
+
|
170
|
+
=begin
|
171
|
+
»in« and »out« provide the main access to edges.
|
172
|
+
|
173
|
+
»in» is a reserved keyword. Therefor its only an alias to `in_e`.
|
174
|
+
|
175
|
+
If called without a parameter, all connected edges are retrieved.
|
176
|
+
|
177
|
+
If called with a string, symbol or class, the edge-class is resolved and even inherent
|
178
|
+
edges are retrieved.
|
179
|
+
|
180
|
+
=end
|
181
|
+
|
182
|
+
def in_e edge_name= nil
|
183
|
+
detect_edges :in, edge_name
|
184
|
+
end
|
185
|
+
|
186
|
+
alias_method :in, :in_e
|
187
|
+
|
188
|
+
def out edge_name = nil
|
189
|
+
detect_edges :out, edge_name
|
190
|
+
end
|
191
|
+
=begin
|
192
|
+
Retrieves connected edges
|
193
|
+
|
194
|
+
The basic usage is to fetch all/ incoming/ outgoing edges
|
195
|
+
|
196
|
+
Model-Instance.edges :in :out | :both, :all
|
197
|
+
|
198
|
+
One can filter specific edges by providing parts of the edge-name
|
199
|
+
|
200
|
+
Model-Instance.edges /sector/, :in
|
201
|
+
Model-Instance.edges :out, /sector/
|
202
|
+
Model-Instance.edges /sector/
|
203
|
+
Model-Instance.edges :in
|
204
|
+
|
205
|
+
|
206
|
+
|
207
|
+
The method returns an array of expands edges.
|
208
|
+
|
209
|
+
»in_edges« and »out_edges« are shortcuts to »edges :in« and »edges :out«
|
210
|
+
|
211
|
+
Its easy to expand the result:
|
212
|
+
tg.out( :ohlc).out.out_edges
|
213
|
+
=> [["#102:11032", "#121:0"]]
|
214
|
+
tg.out( :ohlc).out.out_edges.from_orient
|
215
|
+
=> [[#<TG::GRID_OF:0x00000002620e38
|
216
|
+
|
217
|
+
this displays the out-edges correctly
|
218
|
+
|
219
|
+
whereas
|
220
|
+
tg.out( :ohlc).out.edges( :out)
|
221
|
+
=> [["#101:11032", "#102:11032", "#94:10653", "#121:0"]]
|
222
|
+
|
223
|
+
returns all edges. The parameter (:out) is not recognized, because out is already a nested array.
|
224
|
+
|
225
|
+
this
|
226
|
+
tg.out( :ohlc).first.out.edges( :out)
|
227
|
+
is a workaround, but using in_- and out_edges is more elegant.
|
228
|
+
=end
|
229
|
+
def in_edges
|
230
|
+
edges :in
|
231
|
+
end
|
232
|
+
def out_edges
|
233
|
+
edges :out
|
234
|
+
end
|
235
|
+
|
236
|
+
# def remove
|
237
|
+
# db.delete_vertex self
|
238
|
+
# end
|
239
|
+
=begin
|
240
|
+
Human readable representation of Vertices
|
241
|
+
|
242
|
+
Format: < Classname: Edges, Attributes >
|
243
|
+
=end
|
244
|
+
def to_human
|
245
|
+
count_and_display_classes = ->(array){array.map(&:class)&.group_by(&:itself)&.transform_values(&:count)}
|
246
|
+
|
247
|
+
the_ins = count_and_display_classes[ in_e]
|
248
|
+
the_outs = count_and_display_classes[ out]
|
249
|
+
|
250
|
+
in_and_out = in_edges.empty? ? "" : "in: #{the_ins}, "
|
251
|
+
in_and_out += out_edges.empty? ? "" : "out: #{the_outs}, "
|
252
|
+
|
253
|
+
|
254
|
+
#Default presentation of ActiveOrient::Model-Objects
|
255
|
+
|
256
|
+
"<#{self.class.to_s.demodulize}[#{rid}]: " + in_and_out + content_attributes.map do |attr, value|
|
257
|
+
v= case value
|
258
|
+
when ActiveOrient::Model
|
259
|
+
"< #{self.class.to_s.demodulize}: #{value.rid} >"
|
260
|
+
when OrientSupport::Array
|
261
|
+
value.to_s
|
262
|
+
# value.rrid #.to_human #.map(&:to_human).join("::")
|
263
|
+
else
|
264
|
+
value.from_orient
|
265
|
+
end
|
266
|
+
"%s : %s" % [ attr, v] unless v.nil?
|
267
|
+
end.compact.sort.join(', ') + ">".gsub('"' , ' ')
|
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
|
371
|
+
end
|