active-orient 0.42 → 0.79
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/Gemfile +13 -5
- data/Guardfile +12 -4
- data/README.md +67 -280
- data/VERSION +1 -1
- data/active-orient.gemspec +6 -5
- data/bin/active-orient-0.6.gem +0 -0
- data/bin/active-orient-console +85 -0
- data/config/boot.rb +72 -1
- data/config/config.yml +10 -0
- data/config/connect.yml +9 -4
- data/examples/books.rb +92 -40
- data/examples/streets.rb +89 -85
- data/examples/test_commands.rb +97 -0
- data/examples/test_commands_2.rb +59 -0
- data/examples/test_commands_3.rb +55 -0
- data/examples/test_commands_4.rb +33 -0
- data/examples/time_graph.md +162 -0
- data/lib/active-orient.rb +75 -9
- data/lib/base.rb +238 -169
- data/lib/base_properties.rb +68 -60
- data/lib/class_utils.rb +226 -0
- data/lib/database_utils.rb +98 -0
- data/lib/init.rb +79 -0
- data/lib/java-api.rb +442 -0
- data/lib/jdbc.rb +211 -0
- data/lib/model/custom.rb +26 -0
- data/lib/model/edge.rb +70 -0
- data/lib/model/model.rb +134 -0
- data/lib/model/the_class.rb +607 -0
- data/lib/model/the_record.rb +266 -0
- data/lib/model/vertex.rb +236 -0
- data/lib/orientdb_private.rb +48 -0
- data/lib/other.rb +371 -0
- data/lib/railtie.rb +68 -0
- data/lib/rest/change.rb +147 -0
- data/lib/rest/create.rb +279 -0
- data/lib/rest/delete.rb +134 -0
- data/lib/rest/operations.rb +211 -0
- data/lib/rest/read.rb +171 -0
- data/lib/rest/rest.rb +112 -0
- data/lib/rest_disabled.rb +24 -0
- data/lib/support/logging.rb +38 -0
- data/lib/support/orient.rb +196 -0
- data/lib/support/orientquery.rb +469 -0
- data/rails.md +154 -0
- data/rails/activeorient.rb +32 -0
- data/rails/config.yml +10 -0
- data/rails/connect.yml +17 -0
- metadata +65 -24
- data/active-orient-0.4.gem +0 -0
- data/active-orient-0.41.gem +0 -0
- data/lib/model.rb +0 -468
- data/lib/orient.rb +0 -98
- data/lib/query.rb +0 -88
- data/lib/rest.rb +0 -1059
- data/lib/support.rb +0 -372
- data/test.rb +0 -4
- data/usecase.md +0 -91
data/lib/jdbc.rb
ADDED
@@ -0,0 +1,211 @@
|
|
1
|
+
require 'orientdb'
|
2
|
+
require_relative "database_utils.rb" #common methods without rest.specific content
|
3
|
+
require_relative "class_utils.rb" #common methods without rest.specific content
|
4
|
+
require_relative "orientdb_private.rb"
|
5
|
+
|
6
|
+
|
7
|
+
module ActiveOrient
|
8
|
+
|
9
|
+
|
10
|
+
class API
|
11
|
+
include OrientSupport::Support
|
12
|
+
include DatabaseUtils
|
13
|
+
include ClassUtils
|
14
|
+
include OrientDbPrivate
|
15
|
+
include OrientDB
|
16
|
+
|
17
|
+
mattr_accessor :logger # borrowed from active_support
|
18
|
+
mattr_accessor :default_server
|
19
|
+
attr_reader :database # Used to read the working database
|
20
|
+
|
21
|
+
#### INITIALIZATION ####
|
22
|
+
|
23
|
+
|
24
|
+
def initialize database: nil, connect: true, preallocate: true
|
25
|
+
self.logger = Logger.new('/dev/stdout') unless logger.present?
|
26
|
+
self.default_server = {
|
27
|
+
:server => 'localhost',
|
28
|
+
:port => 2480,
|
29
|
+
:protocol => 'http',
|
30
|
+
:user => 'root',
|
31
|
+
:password => 'root',
|
32
|
+
:database => 'temp'
|
33
|
+
}.merge default_server.presence || {}
|
34
|
+
@database = database || default_server[:database]
|
35
|
+
@all_classes=[]
|
36
|
+
#puts ["remote:#{default_server[:server]}/#{@database}",
|
37
|
+
# default_server[:user], default_server[:password] ]
|
38
|
+
connect() if connect
|
39
|
+
# @db = DocumentDatabase.connect("remote:#{default_server[:server]}/#{@database}",
|
40
|
+
# default_server[:user], default_server[:password] )
|
41
|
+
ActiveOrient::Model.api = self
|
42
|
+
preallocate_classes if preallocate
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
def db
|
47
|
+
@db
|
48
|
+
end
|
49
|
+
|
50
|
+
# Used for the connection on the server
|
51
|
+
#
|
52
|
+
|
53
|
+
# Used to connect to the database
|
54
|
+
|
55
|
+
def connect
|
56
|
+
|
57
|
+
@db = DocumentDatabase.connect("remote:#{default_server[:server]}/#{@database}",
|
58
|
+
default_server[:user], default_server[:password] )
|
59
|
+
@classes = get_database_classes
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
|
64
|
+
def get_classes *attributes
|
65
|
+
classes= @db.metadata.schema.classes.map{|x| { 'name' => x.name , 'superClass' => x.get_super_class.nil? ? '': x.get_super_class.name } }
|
66
|
+
unless attributes.empty?
|
67
|
+
classes.map{|y| y.select{|v,_| attributes.include?(v)}}
|
68
|
+
else
|
69
|
+
classes
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
def create_classes classes , &b
|
76
|
+
consts = allocate_classes_in_ruby( classes , &b )
|
77
|
+
|
78
|
+
all_classes = consts.is_a?( Array) ? consts.flatten : [consts]
|
79
|
+
get_database_classes(requery: true)
|
80
|
+
selected_classes = all_classes.map do | this_class |
|
81
|
+
this_class unless get_database_classes(requery: false).include?( this_class.ref_name ) rescue nil
|
82
|
+
end.compact.uniq
|
83
|
+
command= selected_classes.map do | database_class |
|
84
|
+
## improper initialized ActiveOrient::Model-classes lack a ref_name class-variable
|
85
|
+
next if database_class.ref_name.blank?
|
86
|
+
c = if database_class.superclass == ActiveOrient::Model || database_class.superclass.ref_name.blank?
|
87
|
+
puts "CREATE CLASS #{database_class.ref_name} "
|
88
|
+
OClassImpl.create @db, database_class.ref_name
|
89
|
+
else
|
90
|
+
puts "CREATE CLASS #{database_class.ref_name} EXTENDS #{database_class.superclass.ref_name}"
|
91
|
+
OClassImpl.create @db, superClass: database_class.ref_name
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# update the internal class hierarchy
|
96
|
+
get_database_classes requery: true
|
97
|
+
# return all allocated classes, no matter whether they had to be created in the DB or not.
|
98
|
+
# keep the format of the input-parameter
|
99
|
+
consts.shift if block_given? && consts.is_a?( Array) # remove the first element
|
100
|
+
# remove traces of superclass-allocations
|
101
|
+
if classes.is_a? Hash
|
102
|
+
consts = Hash[ consts ]
|
103
|
+
consts.each_key{ |x| consts[x].delete_if{|y| y == x} if consts[x].is_a? Array }
|
104
|
+
end
|
105
|
+
consts
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
|
110
|
+
def delete_class o_class
|
111
|
+
@db.schema.drop_class classname(o_class)
|
112
|
+
get_database_classes requery: true
|
113
|
+
end
|
114
|
+
=begin
|
115
|
+
Creates properties and optional an associated index as defined in the provided block
|
116
|
+
create_properties(classname or class, properties as hash){index}
|
117
|
+
|
118
|
+
The default-case
|
119
|
+
create_properties(:my_high_sophisticated_database_class,
|
120
|
+
con_id: {type: :integer},
|
121
|
+
details: {type: :link, linked_class: 'Contracts'}) do
|
122
|
+
contract_idx: :notunique
|
123
|
+
end
|
124
|
+
|
125
|
+
A composite index
|
126
|
+
create_properties(:my_high_sophisticated_database_class,
|
127
|
+
con_id: {type: :integer},
|
128
|
+
symbol: {type: :string}) do
|
129
|
+
{name: 'indexname',
|
130
|
+
on: [:con_id, :details] # default: all specified properties
|
131
|
+
type: :notunique # default: :unique
|
132
|
+
}
|
133
|
+
end
|
134
|
+
=end
|
135
|
+
|
136
|
+
def create_properties o_class, **all_properties, &b
|
137
|
+
logger.progname = 'JavaApi#CreateProperties'
|
138
|
+
ap = all_properties
|
139
|
+
created_properties = ap.map do |property, specification |
|
140
|
+
puts "specification: #{specification.inspect}"
|
141
|
+
field_type = ( specification.is_a?( Hash) ? specification[:type] : specification ).downcase.to_sym rescue :string
|
142
|
+
the_other_class = specification.is_a?(Hash) ? specification[:other_class] : nil
|
143
|
+
other_class = if the_other_class.present?
|
144
|
+
@db.get_class( the_other_class)
|
145
|
+
end
|
146
|
+
index = ap.is_a?(Hash) ? ap[:index] : nil
|
147
|
+
if other_class.present?
|
148
|
+
@db.get_class(classname(o_class)).add property,[ field_type, other_class ], { :index => index }
|
149
|
+
else
|
150
|
+
@db.get_class(classname(o_class)).add property, field_type, { :index => index }
|
151
|
+
end
|
152
|
+
end
|
153
|
+
if block_given?
|
154
|
+
attr = yield
|
155
|
+
index_parameters = case attr
|
156
|
+
when String, Symbol
|
157
|
+
{ name: attr }
|
158
|
+
when Hash
|
159
|
+
{ name: attr.keys.first , type: attr.values.first, on: all_properties.keys.map(&:to_s) }
|
160
|
+
else
|
161
|
+
nil
|
162
|
+
end
|
163
|
+
create_index o_class, **index_parameters unless index_parameters.blank?
|
164
|
+
end
|
165
|
+
created_properties.size # return_value
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
def get_properties o_class
|
170
|
+
@db.get_class(classname(o_class)).propertiesMap
|
171
|
+
end
|
172
|
+
|
173
|
+
|
174
|
+
|
175
|
+
def create_index o_class, name:, on: :automatic, type: :unique
|
176
|
+
logger.progname = 'JavaApi#CreateIndex'
|
177
|
+
begin
|
178
|
+
c = @db.get_class( classname( o_class ))
|
179
|
+
index = if on == :automatic
|
180
|
+
nil # not implemented
|
181
|
+
elsif on.is_a? Array
|
182
|
+
c.createIndex name.to_s, INDEX_TYPES[type], *on
|
183
|
+
else
|
184
|
+
c.createIndex name.to_s, INDEX_TYPES[type], on
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def create_record o_class, attributes: {}
|
190
|
+
logger.progname = 'HavaApi#CreateRecord'
|
191
|
+
attributes = yield if attributes.empty? && block_given?
|
192
|
+
new_record = insert_document( o_class, attributes.to_orient )
|
193
|
+
|
194
|
+
|
195
|
+
end
|
196
|
+
alias create_document create_record
|
197
|
+
|
198
|
+
def insert_document o_class, attributes
|
199
|
+
d = Document.create @db, classname(o_class), **attributes
|
200
|
+
d.save
|
201
|
+
ActiveOrient::Model.get_model_class(classname(o_class)).new attributes.merge( { "@rid" => d.rid,
|
202
|
+
"@version" => d.version,
|
203
|
+
"@type" => 'd',
|
204
|
+
"@class" => classname(o_class) } )
|
205
|
+
|
206
|
+
|
207
|
+
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
data/lib/model/custom.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
module CustomClass
|
2
|
+
=begin
|
3
|
+
The REST-Interface does not work with "select from SomeClass where a_property like 'pattern%' "
|
4
|
+
|
5
|
+
This is rewritten as
|
6
|
+
|
7
|
+
SomeClass.like "name = D*", order: 'asc'
|
8
|
+
|
9
|
+
The order-argument is optional, "asc" is the default.
|
10
|
+
This Primitiv-Version only accepts the wildcards "*" and "%" at the end of the seach-string.
|
11
|
+
The Wildcard can be omitted.
|
12
|
+
|
13
|
+
The method does not accept further arguments.
|
14
|
+
=end
|
15
|
+
def like operation, order: 'asc'
|
16
|
+
# remove all spaces and split the resulting word
|
17
|
+
p, s = operation.gsub(/\s+/, "").split("=")
|
18
|
+
if ["%","*"].include?(s[-1])
|
19
|
+
s.chop!
|
20
|
+
end
|
21
|
+
|
22
|
+
q = OrientSupport::OrientQuery.new where: { "#{p}.left(#{s.length})" => s } ,order: { p => order }
|
23
|
+
query_database q
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
data/lib/model/edge.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
class E < ActiveOrient::Model
|
2
|
+
## class methods
|
3
|
+
class << self
|
4
|
+
=begin
|
5
|
+
Establish constrains on Edges
|
6
|
+
|
7
|
+
After applying this method Edges are uniq!
|
8
|
+
|
9
|
+
Creates individual indices for child-classes if applied to the class itself.
|
10
|
+
=end
|
11
|
+
def uniq_index
|
12
|
+
create_property :in, type: :link, linked_class: :V
|
13
|
+
create_property :out, type: :link, linked_class: :V
|
14
|
+
create_index "#{ref_name}_idx", on: [ :in, :out ]
|
15
|
+
end
|
16
|
+
|
17
|
+
=begin
|
18
|
+
Instantiate a new Edge between two Vertices
|
19
|
+
|
20
|
+
=end
|
21
|
+
def create from:, to: , attributes: {}, transaction: false
|
22
|
+
return nil if from.blank? || to.blank?
|
23
|
+
statement = "CREATE EDGE #{ref_name} from #{from.to_or} to #{to.to_or}"
|
24
|
+
transaction = true if [:fire, :complete, :run].include?(transaction)
|
25
|
+
ir= db.execute( transaction: transaction ){ statement }
|
26
|
+
from.reload! # get last version
|
27
|
+
to.is_a?(Array)? to.each( &:reload! ) : to.reload!
|
28
|
+
ir
|
29
|
+
|
30
|
+
rescue ArgumentError => e
|
31
|
+
logger.error{ "wrong parameters #{keyword_arguments} \n\t\t required: from: , to: , attributes:\n\t\t Edge is NOT created"}
|
32
|
+
end
|
33
|
+
|
34
|
+
=begin
|
35
|
+
Fires a "delete edge" command to the database.
|
36
|
+
|
37
|
+
The where statement can be empty ( "" or {}"), then all edges are removed
|
38
|
+
|
39
|
+
The rid-cache is resetted
|
40
|
+
|
41
|
+
:call-seq:
|
42
|
+
delete where:
|
43
|
+
=end
|
44
|
+
def delete where:
|
45
|
+
|
46
|
+
db.execute { "delete edge #{ref_name} #{db.compose_where(where)}" }
|
47
|
+
reset_rid_store
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end # class methods
|
52
|
+
|
53
|
+
### instance methods ###
|
54
|
+
|
55
|
+
=begin
|
56
|
+
Removes the actual ActiveOrient::Model-Edge-Object
|
57
|
+
|
58
|
+
This method overloads the unspecified ActiveOrient::Model#remove-Method
|
59
|
+
=end
|
60
|
+
def remove
|
61
|
+
# remove works on record-level
|
62
|
+
db.delete_edge self
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_human
|
66
|
+
displayed_attributes = content_attributes.reject{|k,_| [:in, :out].include?(k) }
|
67
|
+
"<#{self.class.to_s.demodulize}[#{rrid}] -i-> ##{ attributes[:in].rid} #{displayed_attributes.to_human} -o-> #{out.rrid}>"
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
data/lib/model/model.rb
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
require_relative "the_class.rb"
|
2
|
+
require_relative "the_record.rb"
|
3
|
+
require_relative "custom.rb"
|
4
|
+
module ActiveOrient
|
5
|
+
class Model < ActiveOrient::Base
|
6
|
+
|
7
|
+
include BaseProperties
|
8
|
+
include ModelRecord # For objects (file: lib/record.rb)
|
9
|
+
extend CustomClass # for customized class-methods aka like
|
10
|
+
extend ModelClass # For classes
|
11
|
+
|
12
|
+
=begin
|
13
|
+
Either retrieves the object from the rid_store or loads it from the DB.
|
14
|
+
|
15
|
+
Example:
|
16
|
+
ActiveOrient::Model.autoload_object "#00:00"
|
17
|
+
|
18
|
+
|
19
|
+
The rid_store is updated!
|
20
|
+
|
21
|
+
_Todo:_ fetch for version in the db and load the object only if a change is detected
|
22
|
+
|
23
|
+
_Note:_ This function is not located in ModelClass since it needs to use @@rid_store
|
24
|
+
=end
|
25
|
+
|
26
|
+
def self.autoload_object rid
|
27
|
+
rid = rid[1..-1] if rid[0]=='#'
|
28
|
+
if rid.rid?
|
29
|
+
if @@rid_store[rid].present?
|
30
|
+
@@rid_store[rid] # return_value
|
31
|
+
else
|
32
|
+
get(rid)
|
33
|
+
end
|
34
|
+
else
|
35
|
+
logger.progname = "ActiveOrient::Model#AutoloadObject"
|
36
|
+
logger.info{"#{rid} is not a valid rid."}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
## used for active-model-compatibility
|
41
|
+
def persisted?
|
42
|
+
true
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
|
47
|
+
=begin
|
48
|
+
Based on the parameter rid (as "#{a}:{b}" or "{a}:{b}") the cached value is used if found.
|
49
|
+
Otherwise the provided Block is executed, which is responsible for the allocation of a new dataset
|
50
|
+
|
51
|
+
i.e.
|
52
|
+
ActiveOrient::Model.use_or_allocated my_rid do
|
53
|
+
ActiveOrient::Model.orientdb_class(name: raw_data['@class']).new raw_data
|
54
|
+
end
|
55
|
+
|
56
|
+
=end
|
57
|
+
|
58
|
+
def self.use_or_allocate rid
|
59
|
+
cached_obj = get_rid( rid )
|
60
|
+
if cached_obj.present?
|
61
|
+
cached_obj
|
62
|
+
else
|
63
|
+
yield
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
def self._to_partial_path #:nodoc:
|
69
|
+
@_to_partial_path ||= begin
|
70
|
+
element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(name))
|
71
|
+
collection = ActiveSupport::Inflector.tableize(name)
|
72
|
+
"#{collection}/#{element}".freeze
|
73
|
+
end
|
74
|
+
end
|
75
|
+
## to prevent errors when calling to_a
|
76
|
+
def to_ary # :nodoc:
|
77
|
+
attributes.to_a
|
78
|
+
end
|
79
|
+
|
80
|
+
def document # :nodoc:
|
81
|
+
@d
|
82
|
+
end
|
83
|
+
|
84
|
+
=begin
|
85
|
+
Deletes the database class and removes the ruby-class
|
86
|
+
=end
|
87
|
+
def self.delete_class what= :all
|
88
|
+
orientdb.delete_class( self ) if what == :all # remove the database-class
|
89
|
+
## namespace is defined in config/boot
|
90
|
+
ns = namespace.to_s == 'Object' ? "" : namespace.to_s
|
91
|
+
ns_found = -> ( a_class ) do
|
92
|
+
to_compare = a_class.to_s.split(':')
|
93
|
+
if ns == "" && to_compare.size == 1
|
94
|
+
true
|
95
|
+
elsif to_compare.first == ns
|
96
|
+
true
|
97
|
+
else
|
98
|
+
false
|
99
|
+
end
|
100
|
+
end
|
101
|
+
self.allocated_classes.delete_if{|x,y| x == self.ref_name && ns_found[y]} if allocated_classes.is_a?(Hash)
|
102
|
+
namespace.send(:remove_const, naming_convention.to_sym) if namespace &.send( :const_defined?, naming_convention)
|
103
|
+
end
|
104
|
+
|
105
|
+
# provides an unique accessor on the Class
|
106
|
+
# works with a class-variable, its unique through all Subclasses
|
107
|
+
mattr_accessor :orientdb # points to the instance of the REST-DB-Client used for Administration
|
108
|
+
# i.e. creation and deleting of classes and databases
|
109
|
+
mattr_accessor :db # points to the instance of the Client used for Database-Queries
|
110
|
+
mattr_accessor :api
|
111
|
+
# mattr_accessor :logger ... already inherented from ::Base
|
112
|
+
mattr_accessor :namespace # Namespace in which Model records are initialized, a constant ( defined in config.yml )
|
113
|
+
mattr_accessor :model_dir # path to model-files
|
114
|
+
mattr_accessor :keep_models_without_file
|
115
|
+
mattr_accessor :allocated_classes
|
116
|
+
|
117
|
+
# mattr_accessor :ref_name
|
118
|
+
# Used to read the metadata
|
119
|
+
attr_reader :metadata
|
120
|
+
|
121
|
+
# provides an accessor at class level
|
122
|
+
# (unique on all instances)
|
123
|
+
class << self
|
124
|
+
attr_accessor :ref_name
|
125
|
+
attr_accessor :abstract
|
126
|
+
|
127
|
+
def to_or
|
128
|
+
ref_name.to_or
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,607 @@
|
|
1
|
+
module ModelClass
|
2
|
+
require 'stringio'
|
3
|
+
include OrientSupport::Support
|
4
|
+
|
5
|
+
|
6
|
+
########### CLASS FUNCTIONS ######### SELF ####
|
7
|
+
|
8
|
+
|
9
|
+
######## INITIALIZE A RECORD FROM A CLASS ########
|
10
|
+
|
11
|
+
|
12
|
+
=begin
|
13
|
+
NamingConvention provides a translation from database-names to class-names.
|
14
|
+
|
15
|
+
It can be overwritten to provide different conventions for different classes, eg. Vertexes or edges
|
16
|
+
and to introduce distinct naming-conventions in differrent namespaces
|
17
|
+
|
18
|
+
To overwrite use
|
19
|
+
class Model # < ActiveOrient::Model[:: ...]
|
20
|
+
def self.naming_convention
|
21
|
+
( conversion code )
|
22
|
+
end
|
23
|
+
end
|
24
|
+
=end
|
25
|
+
def naming_convention name=nil
|
26
|
+
nc = name.present?? name.to_s : ref_name
|
27
|
+
if namespace_prefix.present?
|
28
|
+
nc.split(namespace_prefix).last.camelize
|
29
|
+
else
|
30
|
+
nc.camelize
|
31
|
+
end
|
32
|
+
rescue
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
=begin
|
38
|
+
Set the namespace_prefix for database-classes.
|
39
|
+
|
40
|
+
If a namespace is set by
|
41
|
+
ActiveOrient::Init.define_namespace { ModuleName }
|
42
|
+
ActiveOrient translates this to
|
43
|
+
ModuleName::CamelizedClassName
|
44
|
+
The database-class becomes
|
45
|
+
modulename_class_name
|
46
|
+
|
47
|
+
If the namespace is set to a class (Object, ActiveOrient::Model ) namespace_prefix returns an empty string.
|
48
|
+
|
49
|
+
Override to change its behavior
|
50
|
+
=end
|
51
|
+
def namespace_prefix
|
52
|
+
namespace.is_a?(Class )? '' : namespace.to_s.downcase+'_'
|
53
|
+
end
|
54
|
+
=begin
|
55
|
+
orientdb_class is used to refer a ActiveOrient:Model-Object providing its name
|
56
|
+
|
57
|
+
Parameter: name: string or symbol
|
58
|
+
=end
|
59
|
+
|
60
|
+
def orientdb_class name:, superclass: nil # :nodoc: # public method: autoload_class
|
61
|
+
|
62
|
+
ActiveOrient.database_classes[name].presence || ActiveOrient::Model
|
63
|
+
rescue NoMethodError => e
|
64
|
+
logger.error { "Error in orientdb_class: is ActiveOrient.database_classes initialized ? \n\n\n" }
|
65
|
+
logger.error{ e.backtrace.map {|l| " #{l}\n"}.join }
|
66
|
+
Kernel.exit
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
=begin
|
71
|
+
setter method to initialise a dummy ActiveOrient::Model class to enable multi-level
|
72
|
+
access to links and linklists
|
73
|
+
=end
|
74
|
+
|
75
|
+
def link_list *property
|
76
|
+
property.each do |p|
|
77
|
+
|
78
|
+
the_dummy_class = orientdb.allocate_class_in_ruby("dummy_"+p.to_s)
|
79
|
+
the_dummy_class.ref_name = ref_name + "." + p.to_s
|
80
|
+
singleton_class.send :define_method, p do
|
81
|
+
the_dummy_class
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
=begin
|
88
|
+
requires the file specified in the model-dir
|
89
|
+
|
90
|
+
In fact, the model-files are loaded instead of required.
|
91
|
+
Thus, even after recreation of a class (Class.delete_class, ORD.create_class classname)
|
92
|
+
custom methods declared in the model files are present.
|
93
|
+
|
94
|
+
Required modelfiles are gone, if the class is destroyed.
|
95
|
+
|
96
|
+
The directory specified is expanded by the namespace. The parameter itself is the base-dir.
|
97
|
+
|
98
|
+
Example:
|
99
|
+
Namespace: HC
|
100
|
+
model_dir : 'lib/model'
|
101
|
+
searched directory: 'lib/model/hc'
|
102
|
+
|
103
|
+
=end
|
104
|
+
def require_model_file the_directory = nil
|
105
|
+
logger.progname = 'ModelClass#RequireModelFile'
|
106
|
+
the_directory = Pathname( the_directory.presence || ActiveOrient::Model.model_dir ) rescue nil # the_directory is a Pathname
|
107
|
+
return nil if the_directory.nil?
|
108
|
+
if File.exists?( the_directory )
|
109
|
+
model= self.to_s.underscore + ".rb"
|
110
|
+
filename = the_directory + model
|
111
|
+
if File.exists?(filename )
|
112
|
+
if load filename
|
113
|
+
logger.info{ "#{filename} sucessfully loaded" }
|
114
|
+
self #return_value
|
115
|
+
else
|
116
|
+
logger.error{ "#{filename} load error" }
|
117
|
+
nil #return_value
|
118
|
+
end
|
119
|
+
else
|
120
|
+
logger.info{ "model-file not present: #{filename}" }
|
121
|
+
nil #return_value
|
122
|
+
end
|
123
|
+
else
|
124
|
+
logger.info{ "Directory #{ the_directory } not present " }
|
125
|
+
nil #return_value
|
126
|
+
end
|
127
|
+
rescue TypeError => e
|
128
|
+
puts "TypeError: #{e.message}"
|
129
|
+
puts "Working on #{self.to_s} -> #{self.superclass}"
|
130
|
+
puts "Class_hierarchy: #{orientdb.class_hierarchy.inspect}."
|
131
|
+
print e.backtrace.join("\n")
|
132
|
+
raise
|
133
|
+
#
|
134
|
+
end
|
135
|
+
|
136
|
+
########## CREATE ############
|
137
|
+
|
138
|
+
=begin
|
139
|
+
Universal method to create a new record.
|
140
|
+
It's overloaded to create specific kinds, eg. edge and vertex and is called only for abstract classes
|
141
|
+
|
142
|
+
Example:
|
143
|
+
ORD.create_class :test
|
144
|
+
Test.create string_attribute: 'a string', symbol_attribute: :a_symbol, array_attribute: [34,45,67]
|
145
|
+
Test.create link_attribute: Test.create( :a_new_attribute => 'new' )
|
146
|
+
|
147
|
+
=end
|
148
|
+
def create **attributes
|
149
|
+
attributes.merge :created_at => DateTime.new
|
150
|
+
result = db.create_record self, attributes: attributes
|
151
|
+
if result.nil
|
152
|
+
logger.error('Model::Class'){ "Table #{refname}: create failed: #{attributes.inspect}" }
|
153
|
+
elsif block_given?
|
154
|
+
yield result
|
155
|
+
else
|
156
|
+
result # return value
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
=begin
|
161
|
+
Creates or updates a record.
|
162
|
+
Parameter:
|
163
|
+
- set: A hash of attributes to insert or update unconditionally
|
164
|
+
- where: A string or hash as condition which should return just one record.
|
165
|
+
|
166
|
+
The where-part should be covered with an unique-index.
|
167
|
+
|
168
|
+
returns the affected record
|
169
|
+
=end
|
170
|
+
def upsert set: nil, where:
|
171
|
+
set = where if set.nil?
|
172
|
+
db.upsert self, set: set, where: where
|
173
|
+
end
|
174
|
+
=begin
|
175
|
+
Sets a value to certain attributes, overwrites existing entries, creates new attributes if nessesary
|
176
|
+
|
177
|
+
IB::Account.update_all connected: false
|
178
|
+
IB::Account.update_all where: "account containsText 'F'", set:{ connected: false }
|
179
|
+
|
180
|
+
**note: By calling UpdateAll, all records of the Class previously stored in the rid-cache are removed from the cache. Thus autoload gets the updated records.
|
181
|
+
=end
|
182
|
+
|
183
|
+
def update_all where: {} , set: {}, **arg
|
184
|
+
if where.empty?
|
185
|
+
set.merge! arg
|
186
|
+
end
|
187
|
+
db.update_records self, set: set, where: where
|
188
|
+
|
189
|
+
end
|
190
|
+
#
|
191
|
+
# removes a property from the collection (where given) or the entire class
|
192
|
+
def remove attribute, where:{}
|
193
|
+
db.update_records self, remove: attribute, where: where
|
194
|
+
end
|
195
|
+
|
196
|
+
=begin
|
197
|
+
Create a Property in the Schema of the Class and optionaly create an automatic index
|
198
|
+
|
199
|
+
Examples:
|
200
|
+
|
201
|
+
create_property :customer_id, type: integer, index: :unique
|
202
|
+
create_property( :name, type: :string ) { :unique }
|
203
|
+
create_property :in, type: :link, linked_class: V (used by edges)
|
204
|
+
|
205
|
+
:call-seq: create_property(field (required),
|
206
|
+
type: :a_supported_type',
|
207
|
+
linked_class: nil
|
208
|
+
|
209
|
+
supported types:
|
210
|
+
:bool :double :datetime :float :decimal
|
211
|
+
:embedded_list = :list :embedded_map = :map :embedded_set = :set
|
212
|
+
:int :integer :link_list :link_map :link_set
|
213
|
+
|
214
|
+
If `:list`, `:map`, `:set`, `:link`, `:link_list`, `:link_map` or `:link_set` is specified
|
215
|
+
a `linked_class:` parameter can be specified. Argument is the OrientDB-Class-Constant
|
216
|
+
=end
|
217
|
+
def create_property field, type: :integer, index: nil, **args
|
218
|
+
arguments = args.values.map do |y|
|
219
|
+
if y.is_a?(Class) && ActiveOrient.database_classes.values.include?(y)
|
220
|
+
y.ref_name
|
221
|
+
elsif ActiveOrient.database_classes.keys.include?(y.to_s)
|
222
|
+
y
|
223
|
+
else
|
224
|
+
puts ActiveOrient.database_classes.inspect
|
225
|
+
puts "YY : #{y.to_s} #{y.class}"
|
226
|
+
raise ArgumentError , "database class #{y.to_s} not allocated"
|
227
|
+
end
|
228
|
+
end.compact.join(',')
|
229
|
+
|
230
|
+
supported_types = {
|
231
|
+
:bool => "BOOLEAN",
|
232
|
+
:double => "BYTE",
|
233
|
+
:datetime => "DATE",
|
234
|
+
:float => "FLOAT",
|
235
|
+
:decimal => "DECIMAL",
|
236
|
+
:embedded_list => "EMBEDDEDLIST",
|
237
|
+
:list => "EMBEDDEDLIST",
|
238
|
+
:embedded_map => "EMBEDDEDMAP",
|
239
|
+
:map => "EMBEDDEDMAP",
|
240
|
+
:embedded_set => "EMBEDDEDSET",
|
241
|
+
:set => "EMBEDDEDSET",
|
242
|
+
:string => "STRING",
|
243
|
+
:int => "INTEGER",
|
244
|
+
:integer => "INTEGER",
|
245
|
+
:link => "LINK",
|
246
|
+
:link_list => "LINKLIST",
|
247
|
+
:link_map => "LINKMAP",
|
248
|
+
:link_set => "LINKSET",
|
249
|
+
}
|
250
|
+
|
251
|
+
## if the »type« argument is a string, it is used unchanged
|
252
|
+
type = supported_types[type] if type.is_a?(Symbol)
|
253
|
+
|
254
|
+
raise ArgumentError , "unsupported type" if type.nil?
|
255
|
+
s= " CREATE PROPERTY #{ref_name}.#{field} #{type} #{arguments}"
|
256
|
+
puts s
|
257
|
+
db.execute { s }
|
258
|
+
|
259
|
+
i = block_given? ? yield : index
|
260
|
+
## supported format of block: index: { name: 'something' , on: :automatic, type: :unique }
|
261
|
+
## or { name: 'something' , on: :automatic, type: :unique } #
|
262
|
+
## or { some_name: :unique } # manual index
|
263
|
+
## or { :unique } # automatic index
|
264
|
+
if i.is_a? Hash
|
265
|
+
att= i.key( :index ) ? i.values.first : i
|
266
|
+
name, on, type = if att.size == 1 && att[:type].nil?
|
267
|
+
[att.keys.first, field, att.values.first ]
|
268
|
+
else
|
269
|
+
[ att[:name] || field , att[:on] || field , att[:type] || :unique ]
|
270
|
+
end
|
271
|
+
create_index( name , on: on, type: type)
|
272
|
+
elsif i.is_a?(Symbol) || i.is_a?(String)
|
273
|
+
create_index field, type: i
|
274
|
+
end
|
275
|
+
|
276
|
+
# orientdb.create_property self, field, **keyword_arguments, &b
|
277
|
+
end
|
278
|
+
|
279
|
+
# Create more Properties in the Schema of the Class
|
280
|
+
|
281
|
+
def create_properties argument_hash, &b
|
282
|
+
orientdb.create_properties self, argument_hash, &b
|
283
|
+
end
|
284
|
+
|
285
|
+
|
286
|
+
# Add an Index
|
287
|
+
def create_index name, **attributes
|
288
|
+
orientdb.create_index self, name: name, **attributes
|
289
|
+
end
|
290
|
+
|
291
|
+
# list all Indexes
|
292
|
+
def indexes
|
293
|
+
properties[:indexes]
|
294
|
+
end
|
295
|
+
########## GET ###############
|
296
|
+
|
297
|
+
def classname # :nodoc: #
|
298
|
+
ref_name
|
299
|
+
end
|
300
|
+
|
301
|
+
# get elements by rid
|
302
|
+
|
303
|
+
def get rid
|
304
|
+
if @excluded.blank?
|
305
|
+
db.get_record(rid)
|
306
|
+
else
|
307
|
+
db.execute{ "select expand( @this.exclude( #{@excluded.map(&:to_or).join(",")})) from #{rid} "}
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
# get all the elements of the class
|
312
|
+
|
313
|
+
def all
|
314
|
+
db.get_records from: self
|
315
|
+
end
|
316
|
+
|
317
|
+
# get the first element of the class
|
318
|
+
|
319
|
+
def first where: {}
|
320
|
+
db.get_records(from: self, where: where, limit: 1).pop
|
321
|
+
end
|
322
|
+
|
323
|
+
# get the last element of the class
|
324
|
+
|
325
|
+
def last where: {}
|
326
|
+
db.get_records(from: self, where: where, order: {"@rid" => 'desc'}, limit: 1).pop
|
327
|
+
end
|
328
|
+
# Used to count of the elements in the class
|
329
|
+
|
330
|
+
def count **args
|
331
|
+
orientdb.count from: self, **args
|
332
|
+
end
|
333
|
+
|
334
|
+
# Get the properties of the class
|
335
|
+
|
336
|
+
def properties
|
337
|
+
object = orientdb.get_class_properties self
|
338
|
+
#HashWithIndifferentAccess.new :properties => object['properties'], :indexes => object['indexes']
|
339
|
+
{:properties => object['properties'], :indexes => object['indexes']}
|
340
|
+
end
|
341
|
+
alias get_class_properties properties
|
342
|
+
|
343
|
+
# Print the properties of the class
|
344
|
+
|
345
|
+
def print_properties
|
346
|
+
orientdb.print_class_properties self
|
347
|
+
end
|
348
|
+
|
349
|
+
=begin
|
350
|
+
»GetRecords« uses the REST-Interface to query the database. The alternative »QueryDatabase« submits
|
351
|
+
the query via Batch.
|
352
|
+
|
353
|
+
Both methods rely on OrientSupport::OrientQuery and its capacity to support complex query-builds.
|
354
|
+
The method requires a hash of arguments. The following keys are supported:
|
355
|
+
|
356
|
+
*projection:*
|
357
|
+
|
358
|
+
SQL-Queries use »select« to specify a projection (ie. `select sum(a), b+5 as z from class where ...`)
|
359
|
+
|
360
|
+
In ruby »select« is a method of enumeration. To specify anything etween »select« and »from« in the query-string
|
361
|
+
we use »projection«, which acceps different arguments
|
362
|
+
|
363
|
+
projection: a_string --> inserts the sting as it appears
|
364
|
+
projection: an OrientSupport::OrientQuery-Object --> performs a sub-query and uses the result for further querying though the given parameters.
|
365
|
+
projection: [a, b, c] --> "a, b, c" (inserts a comma-separated list)
|
366
|
+
projection: {a: b, "sum(x)" => f} --> "a as b, sum(x) as f" (renames properties and uses functions)
|
367
|
+
|
368
|
+
*distinct:*
|
369
|
+
|
370
|
+
Constructs a query like »select distinct(property) [as property] from ...«
|
371
|
+
|
372
|
+
distinct: :property --> the result is mapped to the property »distinct«.
|
373
|
+
distinct: [:property] --> the result replaces the property
|
374
|
+
distinct: {property: :some_name} --> the result is mapped to ModelInstance.some_name
|
375
|
+
|
376
|
+
*order:*
|
377
|
+
|
378
|
+
Sorts the result-set. If new properties were introduced via select:, distinct: etc. Sorting takes place on these properties
|
379
|
+
|
380
|
+
order: :property {property: asc, property: desc}[property, property, .. ](orderdirection is 'asc')
|
381
|
+
|
382
|
+
|
383
|
+
Further supported Parameter:
|
384
|
+
|
385
|
+
group_by
|
386
|
+
skip
|
387
|
+
limit
|
388
|
+
unwind
|
389
|
+
|
390
|
+
see orientdb- documentation (https://orientdb.com/docs/last/SQL-Query.html)
|
391
|
+
|
392
|
+
*query:*
|
393
|
+
|
394
|
+
Instead of providing the parameter to »get_records«, a OrientSupport::OrientQuery can build and
|
395
|
+
tested prior to the method-call. The OrientQuery-Object is then provided with the query-parameter. I.e.
|
396
|
+
|
397
|
+
q = OrientSupport::OrientQuery.new
|
398
|
+
ORD.create_class :test_model
|
399
|
+
q.from TestModel
|
400
|
+
q.where {name: 'Thomas'}
|
401
|
+
count = TestModel.count query: q
|
402
|
+
q.limit 10
|
403
|
+
0.step(count,10) do |x|
|
404
|
+
q.skip = x
|
405
|
+
puts TestModel.get_documents(query: q).map{|x| x.adress }.join('\t')
|
406
|
+
end
|
407
|
+
prints a Table with 10 columns.
|
408
|
+
=end
|
409
|
+
|
410
|
+
def get_records **args
|
411
|
+
db.get_records(from: self, **args){self}
|
412
|
+
end
|
413
|
+
alias get_documents get_records
|
414
|
+
|
415
|
+
|
416
|
+
def custom_where search_string
|
417
|
+
q = OrientSupport::OrientQuery.new from: self, where: search_string
|
418
|
+
#puts q.compose
|
419
|
+
query_database q
|
420
|
+
end
|
421
|
+
=begin
|
422
|
+
Performs a query on the Class and returns an Array of ActiveOrient:Model-Records.
|
423
|
+
|
424
|
+
Example:
|
425
|
+
Log.where priority: 'high'
|
426
|
+
--> submited database-request: query/hc_database/sql/select from Log where priority = 'high'/-1
|
427
|
+
=> [ #<Log:0x0000000480f7d8 @metadata={ ... }, ...
|
428
|
+
|
429
|
+
Multible arguments are joined via "and" eg
|
430
|
+
Aktie.where symbol: 'TSL, exchange: 'ASX'
|
431
|
+
---> select from aktie where symbol = 'TLS' and exchange = 'ASX'
|
432
|
+
|
433
|
+
|
434
|
+
Where performs a »match-Query« that returns only links to the queries records.
|
435
|
+
These are autoloaded (and reused from the cache). If changed database-records should be obtained,
|
436
|
+
custom_query should be used. It performs a "select form class where ... " query which returns records
|
437
|
+
instead of links.
|
438
|
+
|
439
|
+
Property.custom_where( "'Hamburg' in exchanges.label")
|
440
|
+
|
441
|
+
=end
|
442
|
+
|
443
|
+
def where *attributes
|
444
|
+
query= OrientSupport::OrientQuery.new kind: :match, start:{ class: self.classname }
|
445
|
+
query.match_statements[0].where = attributes unless attributes.empty?
|
446
|
+
# the block contains a result-record :
|
447
|
+
#<ActiveOrient::Model:0x0000000003972e00
|
448
|
+
# @metadata={:type=>"d", :class=>nil, :version=>0, :fieldTypes=>"test_models=x"}, @d=nil,
|
449
|
+
# @attributes={:test_models=>"#29:3", :created_at=>Thu, 28 Mar 2019 10:43:51 +0000}>]
|
450
|
+
# ^...........° -> classname.pluralize
|
451
|
+
query_database( query, set_from: false){| record | record.is_a?(ActiveOrient::Model) ? record : record.send( self.classnamepluralize.to_sym ) }
|
452
|
+
end
|
453
|
+
=begin
|
454
|
+
Performs a Match-Query
|
455
|
+
|
456
|
+
The Query starts at the given ActiveOrient::Model-Class. The where-cause narrows the sample to certain
|
457
|
+
records. In the simplest version this can be returned:
|
458
|
+
|
459
|
+
Industry.match where:{ name: "Communications" }
|
460
|
+
=> #<ActiveOrient::Model::Query:0x00000004309608 @metadata={"type"=>"d", "class"=>nil, "version"=>0, "fieldTypes"=>"Industries=x"}, @attributes={"Industries"=>"#21:1", (...)}>
|
461
|
+
|
462
|
+
The attributes are the return-Values of the Match-Query. Unless otherwise noted, the pluralized Model-Classname is used as attribute in the result-set.
|
463
|
+
|
464
|
+
I.match( where: { name: 'Communications' }).first.Industries
|
465
|
+
|
466
|
+
is the same then
|
467
|
+
Industry.where name: "Communications"
|
468
|
+
|
469
|
+
|
470
|
+
The Match-Query uses this result-set as start for subsequent queries on connected records.
|
471
|
+
These connections are defined in the Block
|
472
|
+
|
473
|
+
var = Industry.match do | query |
|
474
|
+
query.connect :in, count: 2, as: 'Subcategories'
|
475
|
+
puts query.to_s # print the query before sending it to the database
|
476
|
+
query # important: block has to return the query
|
477
|
+
end
|
478
|
+
=> MATCH {class: Industry, as: Industries} <-- {} <-- { as: Subcategories } RETURN Industries, Subcategories
|
479
|
+
|
480
|
+
The result-set has two attributes: Industries and Subcategories, pointing to the filtered datasets.
|
481
|
+
|
482
|
+
By using subsequent »connect« and »statement« method-calls even complex Match-Queries can be clearly constructed.
|
483
|
+
|
484
|
+
=end
|
485
|
+
|
486
|
+
def match where: {}
|
487
|
+
query= OrientSupport::OrientQuery.new kind: :match, start:{ class: self.classname }
|
488
|
+
query.match_statements[0].where = where unless where.empty?
|
489
|
+
if block_given?
|
490
|
+
query_database yield(query), set_from: false
|
491
|
+
else
|
492
|
+
send :where, where
|
493
|
+
end
|
494
|
+
|
495
|
+
end
|
496
|
+
|
497
|
+
|
498
|
+
=begin
|
499
|
+
QueryDatabase sends the Query directly to the database.
|
500
|
+
|
501
|
+
The result is not nessessary an Object of the Class.
|
502
|
+
|
503
|
+
The result can be modified further by passing a block.
|
504
|
+
This is helpful, if a match-statement is used and the records should be autoloaded:
|
505
|
+
|
506
|
+
result = query_database(query, set_from: false){| record | record[ self.classname.pluralize ] }
|
507
|
+
|
508
|
+
This autoloads (fetches from the cache/ or database) the attribute self.classname.pluralize (taken from method: where )
|
509
|
+
|
510
|
+
|
511
|
+
query_database is used on model-level and submits
|
512
|
+
select (...) from class
|
513
|
+
|
514
|
+
#query performs queries on the instance-level and submits
|
515
|
+
select (...) from #{a}:{b}
|
516
|
+
|
517
|
+
=end
|
518
|
+
|
519
|
+
def query_database query, set_from: true
|
520
|
+
query.from self if set_from && query.is_a?(OrientSupport::OrientQuery) && query.from.nil?
|
521
|
+
sql_cmd = -> (command) {{ type: "cmd", language: "sql", command: command }}
|
522
|
+
result = db.execute do
|
523
|
+
query.to_s # sql_cmd[query.to_s]
|
524
|
+
end
|
525
|
+
if block_given?
|
526
|
+
result.is_a?(Array)? result.map{|x| yield x } : yield(result)
|
527
|
+
else
|
528
|
+
result
|
529
|
+
end
|
530
|
+
if result.is_a? Array
|
531
|
+
OrientSupport::Array.new work_on: self, work_with: result
|
532
|
+
else
|
533
|
+
result
|
534
|
+
end # return value
|
535
|
+
end
|
536
|
+
|
537
|
+
########### DELETE ###############
|
538
|
+
|
539
|
+
# Delete a property from the class
|
540
|
+
|
541
|
+
def delete_property field
|
542
|
+
orientdb.delete_property self, field
|
543
|
+
end
|
544
|
+
|
545
|
+
# Delete record(s) specified by their rid's
|
546
|
+
|
547
|
+
def delete_record *rid
|
548
|
+
db.delete_record rid
|
549
|
+
end
|
550
|
+
alias delete_document delete_record
|
551
|
+
|
552
|
+
# Query the database and delete the records of the resultset
|
553
|
+
|
554
|
+
def delete_records where: {}
|
555
|
+
orientdb.delete_records self, where: where
|
556
|
+
end
|
557
|
+
alias delete_documents delete_records
|
558
|
+
|
559
|
+
|
560
|
+
|
561
|
+
##################### EXPERIMENT #################
|
562
|
+
|
563
|
+
=begin
|
564
|
+
Suppose that you created a graph where vertexes month is connected with
|
565
|
+
the vertexes day by the edge TIMEOF.
|
566
|
+
Suppose we want to find all the days in the first month and in the third month..
|
567
|
+
|
568
|
+
Usually we can do in the following way.
|
569
|
+
|
570
|
+
ORD.create_class :month
|
571
|
+
(.. put some records into Month ... )
|
572
|
+
firstmonth = Month.first
|
573
|
+
thirdmonth = Month.all[2]
|
574
|
+
days_firstmonth = firstmonth.out_TIMEOF.map{|x| x.in}
|
575
|
+
days_thirdmonth = thirdmonth.out_TIMEOF.map{|x| x.in}
|
576
|
+
|
577
|
+
However we can obtain the same result with the following command
|
578
|
+
|
579
|
+
Month.add_edge_link name: "days", direction: "out", edge: TIME_OF
|
580
|
+
firstmonth = month.first
|
581
|
+
thirdmonth = month.all[2]
|
582
|
+
days_firstmonth = firstmonth.days
|
583
|
+
days_thirdmonth = thirdmonth.days
|
584
|
+
|
585
|
+
To get their value you can do:
|
586
|
+
thirdmonth.days.value
|
587
|
+
=end
|
588
|
+
|
589
|
+
|
590
|
+
def add_edge_link name:, direction: :out, edge:
|
591
|
+
dir = direction.to_s == "out" ? :out : :in
|
592
|
+
define_method(name.to_sym) do
|
593
|
+
return self["#{dir}_#{edge.classname}"].map{|x| x["in"]}
|
594
|
+
end
|
595
|
+
end
|
596
|
+
|
597
|
+
=begin
|
598
|
+
See http://orientdb.com/docs/2.1/SQL-Alter-Property.html
|
599
|
+
=end
|
600
|
+
|
601
|
+
def alter_property property:, attribute: "DEFAULT", alteration: # :nodoc:
|
602
|
+
orientdb.alter_property self, property: property, attribute: attribute, alteration: alteration
|
603
|
+
end
|
604
|
+
|
605
|
+
|
606
|
+
|
607
|
+
end
|