active-orient 0.3 → 0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/README.md +49 -1
- data/VERSION +1 -1
- data/active-orient.gemspec +1 -1
- data/examples/books.rb +78 -0
- data/examples/streets.rb +129 -0
- data/lib/model.rb +40 -20
- data/lib/rest.rb +139 -52
- data/lib/support.rb +72 -40
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d00a72ebc77faf7333a31a4b5c73f17554ccfc88
|
4
|
+
data.tar.gz: 62cd8f3823a629dba2f10e8b91b7841c9359c29d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bdae72102cee0e3c39c07c74715e9167878e9be90f27d12da827dbae7419c0105adf77bb9cc43ecc6bcd5ae80c1f394bff8795865d3014d70758bc9f562a4d8b
|
7
|
+
data.tar.gz: 54415683a7bda1bfac78d73cf668fc93be875f654de0257e67f9c2a85963ef1315e156c2517b795713a4067635b6269710294a1cc2d74f09069de011a751b5ef
|
data/Gemfile
CHANGED
@@ -3,6 +3,7 @@ gemspec
|
|
3
3
|
gem 'activesupport' , "~>4.2"
|
4
4
|
gem 'activemodel'
|
5
5
|
gem 'rest-client', :git => 'git://github.com/rest-client/rest-client.git'
|
6
|
+
gem 'nokogiri', '~> 1.6.6' #, :git => 'git://github.com/sparklemotion/nokogiri.git'
|
6
7
|
group :development, :test do
|
7
8
|
gem "rspec"
|
8
9
|
gem 'rspec-its'
|
data/README.md
CHANGED
@@ -197,9 +197,57 @@ The Attributes "in" and "out" can be used to move across the graph
|
|
197
197
|
start.e1[0].in.something
|
198
198
|
---> "nice
|
199
199
|
```
|
200
|
+
#### Queries
|
201
|
+
Contrary to traditional SQL-based Databases OrientDB handles subqueries very efficient.
|
202
|
+
In addition, OrientDB supports precompiled statements (let-Blocks).
|
203
|
+
|
204
|
+
ActiveOrient is equipped with a simple QueryGenerator: ActiveSupport::OrientQuery.
|
205
|
+
It works in two modi: a comprehensive and a subsequent one
|
206
|
+
```ruby
|
207
|
+
|
208
|
+
q = OrientSupport::OrientQuery.new
|
209
|
+
q.from = Vertex
|
210
|
+
q.where << a: 2
|
211
|
+
q.where << 'b > 3 '
|
212
|
+
q.distinct = :profession
|
213
|
+
q.order = { :name => :asc }
|
214
|
+
|
215
|
+
```
|
216
|
+
is equivalent to
|
217
|
+
```ruby
|
218
|
+
q = OrientSupport::OrientQuery.new from: Vertex ,
|
219
|
+
where: [{ a: 2 }, 'b > 3 '],
|
220
|
+
distinct: :profession,
|
221
|
+
order: { :name => :asc }
|
222
|
+
q.to_s
|
223
|
+
=> select distinct( profession ) from Vertex where a = 2 and b > 3 order by name asc
|
224
|
+
```
|
225
|
+
Both modes can be mixed.
|
226
|
+
|
227
|
+
If subqueries are nessesary, they can be introduced as OrientSupport::OrientQuery or as »let-block«.
|
228
|
+
```ruby
|
229
|
+
q = OrientSupport::OrientQuery.new from: 'ModelQuery'
|
230
|
+
q.let << "$city = adress.city"
|
231
|
+
q.where = "$city.country.name = 'Italy' OR $city.country.name = 'France'"
|
232
|
+
q.to_s
|
233
|
+
=> select from ModelQuery let $city = adress.city where $city.country.name = 'Italy' OR $city.country.name = 'France'
|
234
|
+
```
|
235
|
+
or
|
236
|
+
```ruby
|
237
|
+
q = OrientSupport::OrientQuery.new
|
238
|
+
q.let << { a: OrientSupport::OrientQuery.new( from: '#5:0' ) }
|
239
|
+
q.let << { b: OrientSupport::OrientQuery.new( from: '#5:1' ) }
|
240
|
+
q.let << '$c= UNIONALL($a,$b) '
|
241
|
+
q.projection << 'expand( $c )'
|
242
|
+
q.to_s
|
243
|
+
=> select expand( $c ) let $a = ( select from #5:0 ), $b = ( select from #5:1 ), $c= UNIONALL($a,$b)
|
244
|
+
```
|
245
|
+
|
246
|
+
|
247
|
+
|
200
248
|
|
201
249
|
#### Execute SQL-Commands
|
202
|
-
|
250
|
+
Sql-commands can be executed as batch
|
203
251
|
|
204
252
|
The ActiveOrient::Query-Class provides a Query-Stack and an Records-Array which keeps the results.
|
205
253
|
The ActiveOrient::Query-Class acts as Parent-Class for aggregated Records (without a @rid), which are ActiveOrient::Model::Myquery Objects. If a Query returns a database-record, the correct ActiveOrient::Model-Class is instantiated.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.4
|
data/active-orient.gemspec
CHANGED
@@ -22,6 +22,6 @@ Gem::Specification.new do |s|
|
|
22
22
|
s.add_development_dependency "bundler", "~> 1.8"
|
23
23
|
s.add_development_dependency "rake", "~> 10.0"
|
24
24
|
s.add_dependency 'activesupport', "~> 4.2"
|
25
|
-
s.add_dependency 'rest-client', "~> 2.0
|
25
|
+
s.add_dependency 'rest-client', "~> 2.0"
|
26
26
|
|
27
27
|
end
|
data/examples/books.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
=begin
|
2
|
+
Books Example
|
3
|
+
|
4
|
+
There are several Books. And there is a Table with Keywords. These are our Vertex-Classes
|
5
|
+
|
6
|
+
The Keywords are associated to the books, this is realized via an Edge »has_content«
|
7
|
+
|
8
|
+
This Example demonstrates how to build a query by using OrientSupport::OrientQuery
|
9
|
+
=end
|
10
|
+
class BooksExample
|
11
|
+
|
12
|
+
def initialize db, rebuild: true
|
13
|
+
if rebuild
|
14
|
+
db.delete_class :book
|
15
|
+
db.delete_class :keyword
|
16
|
+
db.delete_class :has_content
|
17
|
+
db.create_vertex_class :book
|
18
|
+
db.create_vertex_class :keyword
|
19
|
+
db.create_edge_class :has_content
|
20
|
+
ActiveOrient::Model::Keyword.create_property( :item , type: :string, index: :unique )
|
21
|
+
ActiveOrient::Model::Book.create_property( :title, type: :string, index: :unique )
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
def read_samples
|
27
|
+
fill_database = ->(sentence, this_book ) do
|
28
|
+
sentence.split(' ').each do |x|
|
29
|
+
this_word = Keyword.update_or_create where: { item: x }
|
30
|
+
this_edge = HC.create_edge from: this_book, to: this_word if this_word.present?
|
31
|
+
end
|
32
|
+
end
|
33
|
+
words = 'Die Geschäfte in der Industrie im wichtigen US-Bundesstaat New York sind im August so schlecht gelaufen wie seit mehr als sechs Jahren nicht mehr Der entsprechende Empire-State-Index fiel überraschend von plus Punkten im Juli auf minus 14,92 Zähler Dies teilte die New Yorker Notenbank Fed heut mit Bei Werten im positiven Bereich signalisiert das Barometer ein Wachstum Ökonomen hatten eigentlich mit einem Anstieg auf 5,0 Punkte gerechnet'
|
34
|
+
this_book = Book.create title: 'first'
|
35
|
+
fill_database[ words, this_book ]
|
36
|
+
|
37
|
+
words2 = 'Das Bruttoinlandsprodukt BIP in Japan ist im zweiten Quartal mit einer aufs Jahr hochgerechneten Rate von Prozent geschrumpft Zu Jahresbeginn war die nach den USA und China drittgrößte Volkswirtschaft der Welt noch um Prozent gewachsen Der Schwächeanfall wird auch als Rückschlag für Ministerpräsident Shinzo Abe gewertet der das Land mit einem Mix aus billigem Geld und Konjunkturprogramm aus der Flaute bringen will Allerdings wirkte sich die heutige Veröffentlichung auf die Märkten nur wenig aus da Ökonomen mit einem schwächeren zweiten Quartal gerechnet hatten'
|
38
|
+
this_book = Book.create title: 'second'
|
39
|
+
fill_database[ words2, this_book ]
|
40
|
+
end
|
41
|
+
|
42
|
+
def display_books_with *desired_words
|
43
|
+
query = OrientSupport::OrientQuery.new from: Keyword, projection: "expand(in('HasContent'))"
|
44
|
+
q = OrientSupport::OrientQuery.new projection: 'expand( $z )'
|
45
|
+
|
46
|
+
intersects = Array.new
|
47
|
+
desired_words.each_with_index do | word, i |
|
48
|
+
symbol = ( i+97 ).chr # convert 1 -> 'a'
|
49
|
+
query.where = { item: word }
|
50
|
+
q.let << { symbol => query }
|
51
|
+
intersects << "$#{symbol}"
|
52
|
+
end
|
53
|
+
q.let << "$z = Intersect(#{intersects.join(', ')}) "
|
54
|
+
puts "generated Query:"
|
55
|
+
puts q.to_s
|
56
|
+
result = Keyword.query_database q, set_from: false
|
57
|
+
puts "found books: "
|
58
|
+
puts result.map( &:title ).join("; ")
|
59
|
+
puts " -- None -- " if result.empty?
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
if $0 == __FILE__
|
64
|
+
|
65
|
+
require '../config/boot'
|
66
|
+
|
67
|
+
ActiveOrient::OrientDB.default_server = { user: 'hctw', password: 'hc' }
|
68
|
+
ActiveOrient::OrientDB.logger.level = Logger::WARN
|
69
|
+
r = ActiveOrient::OrientDB.new database: 'BookTest'
|
70
|
+
b= BooksExample.new r, rebuild: true
|
71
|
+
|
72
|
+
Book = r.open_class :book
|
73
|
+
Keyword = r.open_class :keyword
|
74
|
+
HC = r.open_class :has_content
|
75
|
+
|
76
|
+
b.read_samples if Keyword.count.zero?
|
77
|
+
b.display_books_with 'Land', 'Quartal'
|
78
|
+
end
|
data/examples/streets.rb
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
=begin
|
2
|
+
Streets Example
|
3
|
+
|
4
|
+
|
5
|
+
We load german cities from wikipedia and parse the document for cities and countries(states).
|
6
|
+
Further we load a collection of popular streetnames from a web-side called »strassen-in-deutschland«
|
7
|
+
|
8
|
+
We define three vertices, State, City and Street.
|
9
|
+
These are filled with the data from the web-sides
|
10
|
+
|
11
|
+
Then we connect the cities through streets just by creating edges.
|
12
|
+
At last we print the connected cities.
|
13
|
+
|
14
|
+
=end
|
15
|
+
module DataImport
|
16
|
+
def read_german_street_names
|
17
|
+
doc = Nokogiri::HTML(open('http://www.strassen-in-deutschland.de/die-haeufigsten-strassennamen-in-deutschland.html'))
|
18
|
+
strassen = doc.css('#strassen-in-deutschland_main a' ) # identified via css-inspector in browser
|
19
|
+
# search for the css and include only links, then display the text-part
|
20
|
+
strassen.children.map( &:to_s )[3..-1] # omit the first three (strassen in deutschland, straßenverzeichnis, straßen)
|
21
|
+
end
|
22
|
+
|
23
|
+
def read_german_cities_from_wikipedia
|
24
|
+
# we extract <li> -elements and use the text until "("
|
25
|
+
#doc.xpath("//li").at(80)
|
26
|
+
# => #<Nokogiri::XML::Element:0x1ba551c name="li" children=[#<Nokogiri::XML::Element:0x1ba533c name="a" attributes=[#<Nokogiri::XML::Attr:0x1ba52d8 name="href" value="/wiki/Angerm%C3%BCnde">, #<Nokogiri::XML::Attr:0x1ba52c4 name="title" value="Angermünde">] children=[#<Nokogiri::XML::Text:0x1ba4c84 "Angermünde">]>, #<Nokogiri::XML::Text:0x1ba4ae0 " (Brandenburg)">]>
|
27
|
+
|
28
|
+
doc = Nokogiri::HTML(open('https://en.wikipedia.org/wiki/List_of_cities_and_towns_in_Germany'))
|
29
|
+
doc.xpath("//li").map{|x| x.text[0 .. x.text.index('(')-2] if x.text.index('(').present? }.compact
|
30
|
+
end
|
31
|
+
|
32
|
+
def read_german_cities_and_states_fom_wikipedia
|
33
|
+
doc =
|
34
|
+
Nokogiri::HTML(open('https://en.wikipedia.org/wiki/List_of_cities_and_towns_in_Germany'))
|
35
|
+
doc.xpath("//li").map do |x|
|
36
|
+
if x.text.index('(').present?
|
37
|
+
[ x.text[0 .. x.text.index('(')-2] , x.text[ x.text.index('(')+1 .. x.text.index(')')-1] ]
|
38
|
+
end
|
39
|
+
end.compact
|
40
|
+
end
|
41
|
+
|
42
|
+
end # module
|
43
|
+
|
44
|
+
class StreetExample
|
45
|
+
|
46
|
+
include DataImport
|
47
|
+
def initialize db, rebuild: true
|
48
|
+
if rebuild
|
49
|
+
ActiveOrient::OrientDB.logger.progname = "StreetsExample#Initialize"
|
50
|
+
db.delete_class :state
|
51
|
+
db.delete_class :city
|
52
|
+
db.delete_class :street
|
53
|
+
db.delete_class :connects
|
54
|
+
db.create_vertex_class :state
|
55
|
+
db.create_vertex_class :city
|
56
|
+
db.create_vertex_class :street
|
57
|
+
db.create_edge_class :connects
|
58
|
+
ActiveOrient::Model::State.create_property( :name, type: :string, index: :unique )
|
59
|
+
ActiveOrient::Model::City.create_properties( { name: { type: :string },
|
60
|
+
state: { type: :link, :linked_class => 'State' } }
|
61
|
+
) do
|
62
|
+
{ citi_idx: :unique }
|
63
|
+
end
|
64
|
+
ActiveOrient::Model::Street.create_property :name , type: :string, index: :notunique
|
65
|
+
ActiveOrient::Model::Connects.create_property :distance, type: :integer, index: :notunique
|
66
|
+
ActiveOrient::OrientDB.logger.info { "Vertex- and Edge-Classes rebuilded" }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
def read_from_web
|
72
|
+
ActiveOrient::OrientDB.logger.progname = "StreetsExample#ReadFromWeb"
|
73
|
+
read_german_cities_and_states_fom_wikipedia.each do |city,state|
|
74
|
+
state = State.update_or_create( where: { name: state }).first
|
75
|
+
City.create name: city, state: state.link
|
76
|
+
end
|
77
|
+
ActiveOrient::OrientDB.logger.info { "#{City.count} Cities imported from Wikipedia " }
|
78
|
+
|
79
|
+
cities_rids = City.all.map &:link
|
80
|
+
read_german_street_names.each_with_index do |street, i|
|
81
|
+
street_record = Street.create name: street
|
82
|
+
count = i
|
83
|
+
cities = Array.new
|
84
|
+
while count < cities_rids.size && cities.size < 5 do
|
85
|
+
cities << cities_rids[count]
|
86
|
+
count = count + i
|
87
|
+
end
|
88
|
+
C.create_edge :from => street_record, :to => cities
|
89
|
+
end
|
90
|
+
ActiveOrient::OrientDB.logger.info { "#{C.count} Edges between Streets and Cities created " }
|
91
|
+
end
|
92
|
+
|
93
|
+
def display_streets_per_state
|
94
|
+
State.all.each do |state|
|
95
|
+
streets= Street.all.map do |street |
|
96
|
+
if street.connects.in.detect{|x| x.state == state }
|
97
|
+
street.name + " verbindet " + street.connects.in.map( &:name ).join('; ')
|
98
|
+
end
|
99
|
+
end.compact
|
100
|
+
unless streets.empty?
|
101
|
+
puts "..................................."
|
102
|
+
puts state.name
|
103
|
+
puts "..................................."
|
104
|
+
puts streets.join("\n")
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
if $0 == __FILE__
|
111
|
+
|
112
|
+
require '../config/boot'
|
113
|
+
require 'open-uri'
|
114
|
+
require 'nokogiri'
|
115
|
+
|
116
|
+
ActiveOrient::OrientDB.default_server= { user: 'hctw', password: 'hc' }
|
117
|
+
r = ActiveOrient::OrientDB.new database: 'StreetTest'
|
118
|
+
ActiveOrient::OrientDB.logger.level = Logger::INFO
|
119
|
+
s= StreetExample.new r, rebuild: true
|
120
|
+
|
121
|
+
City = r.open_class :city
|
122
|
+
State = r.open_class :state
|
123
|
+
Street = r.open_class :street
|
124
|
+
C = r.open_class :connects
|
125
|
+
|
126
|
+
s.read_from_web if City.count.zero?
|
127
|
+
s.display_streets_per_state
|
128
|
+
|
129
|
+
end
|
data/lib/model.rb
CHANGED
@@ -105,20 +105,20 @@ eg . update #link set ...
|
|
105
105
|
end
|
106
106
|
|
107
107
|
|
108
|
-
def method_missing method, *args, &b
|
109
|
-
property= orientdb.get_class_properties( classname )['properties'].detect{|x| x.has_value? method.to_s }
|
110
|
-
puts "method_missing::property"
|
111
|
-
puts property.inspect
|
112
|
-
if property.present?
|
113
|
-
if property['type'] == 'LINKSET'
|
114
|
-
attributes[method] = OrientSupport::Array.new( self )
|
115
|
-
else
|
116
|
-
attributes[method] = ''
|
117
|
-
end
|
118
|
-
else
|
119
|
-
raise NoMethodError
|
120
|
-
end
|
121
|
-
end
|
108
|
+
# def method_missing method, *args, &b
|
109
|
+
# property= orientdb.get_class_properties( classname )['properties'].detect{|x| x.has_value? method.to_s }
|
110
|
+
# puts "method_missing::property"
|
111
|
+
# puts property.inspect
|
112
|
+
# if property.present?
|
113
|
+
# if property['type'] == 'LINKSET'
|
114
|
+
# attributes[method] = OrientSupport::Array.new( self )
|
115
|
+
# else
|
116
|
+
# attributes[method] = ''
|
117
|
+
# end
|
118
|
+
# else
|
119
|
+
# raise NoMethodError
|
120
|
+
# end
|
121
|
+
# end
|
122
122
|
=begin
|
123
123
|
Queries the database and fetches the count of datasets
|
124
124
|
|
@@ -149,11 +149,22 @@ and returns the freshly instantiated Object
|
|
149
149
|
=begin
|
150
150
|
Create a Property in the Schema of the Class
|
151
151
|
:call-seq:
|
152
|
-
self.create_property field (required) , type: 'string', linked_class: nil
|
152
|
+
self.create_property( field (required) , type: 'string', linked_class: nil, index: nil) do
|
153
|
+
index
|
154
|
+
end
|
153
155
|
=end
|
154
156
|
|
155
|
-
def self.
|
156
|
-
orientdb.
|
157
|
+
def self.get_properties
|
158
|
+
object = orientdb.get_class_properties self
|
159
|
+
{:properties => object['properties'] , :indexes => object['indexes'] }
|
160
|
+
end
|
161
|
+
|
162
|
+
def self.create_property field, **keyword_arguments, &b
|
163
|
+
orientdb.create_property self, field, **keyword_arguments, &b
|
164
|
+
end
|
165
|
+
|
166
|
+
def self.create_properties argument_hash, &b
|
167
|
+
orientdb.create_properties self, argument_hash, &b
|
157
168
|
end
|
158
169
|
|
159
170
|
def self.create_link name, class_name
|
@@ -167,15 +178,23 @@ Only if the Class inherents from »E«
|
|
167
178
|
Instantiate a new Edge betwen two Vertices
|
168
179
|
|
169
180
|
Parameter: unique: (true) In case of an existing Edge just update its Properties.
|
181
|
+
The parameters »from« and »to« can take a list of model-records. Then subsequent edges are created.
|
170
182
|
:call-seq:
|
171
183
|
self.create_edge from: , to: , unique: false, attributes:{}
|
172
184
|
=end
|
173
185
|
def self.create_edge **keyword_arguments
|
174
186
|
o=orientdb.nexus_edge self, **keyword_arguments
|
175
|
-
[:from,:to].each{|y| keyword_arguments[y].reload! }
|
187
|
+
[:from,:to].each{|y| keyword_arguments[y].is_a?(Array) ? keyword_arguments[y].each( &:reload! ): keyword_arguments[y].reload! }
|
176
188
|
o # return_value
|
177
189
|
end
|
178
190
|
|
191
|
+
def self.query_database query, set_from: true
|
192
|
+
query.from self if set_from && query.is_a?( OrientSupport::OrientQuery ) && query.from.nil?
|
193
|
+
sql_cmd = -> (command) { { type: "cmd", language: "sql", command: command } }
|
194
|
+
orientdb.execute do
|
195
|
+
[ sql_cmd[ query.to_s ] ]
|
196
|
+
end
|
197
|
+
end
|
179
198
|
def query q
|
180
199
|
a= ActiveOrient::Query.new
|
181
200
|
a.queries << q
|
@@ -243,7 +262,7 @@ prints a Table with 10 columns.
|
|
243
262
|
=end
|
244
263
|
|
245
264
|
def self.get_documents **args , &b
|
246
|
-
orientdb.get_documents from:
|
265
|
+
orientdb.get_documents from: self, **args, &b
|
247
266
|
|
248
267
|
end
|
249
268
|
=begin
|
@@ -258,7 +277,8 @@ Example:
|
|
258
277
|
|
259
278
|
=end
|
260
279
|
def self.where attributes = {}
|
261
|
-
|
280
|
+
q = OrientSupport::OrientQuery.new where: attributes
|
281
|
+
query_database q
|
262
282
|
end
|
263
283
|
=begin
|
264
284
|
|
data/lib/rest.rb
CHANGED
@@ -358,26 +358,33 @@ The parameter o_class can be either a class or a string
|
|
358
358
|
|
359
359
|
def nexus_edge o_class , attributes: {}, from:, to:, unique: false
|
360
360
|
logger.progname = "ActiveOrient::OrientDB#NexusEdge"
|
361
|
-
if
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
361
|
+
if from.is_a? Array
|
362
|
+
from.map{|f| nexus_edge o_class, attributes: attributes, from: f, to: to, unique: unique }
|
363
|
+
elsif to.is_a? Array
|
364
|
+
to.map{|t| nexus_edge o_class, attributes: attributes, from: from, to: t, unique: unique }
|
365
|
+
else
|
366
|
+
|
367
|
+
if unique
|
368
|
+
wwhere = { out: from.to_orient, in: to.to_orient }.merge(attributes.to_orient)
|
369
|
+
existing_edge = get_documents( from: o_class, where: wwhere )
|
370
|
+
if existing_edge.first.is_a?( ActiveOrient::Model )
|
371
|
+
logger.debug { "reusing edge #{class_name(o_class)} from #{from.to_orient} to #{to.to_orient} " }
|
372
|
+
return existing_edge.first
|
373
|
+
end
|
374
|
+
end
|
375
|
+
logger.debug { "creating edge #{class_name(o_class)} from #{from.to_orient} to #{to.to_orient} " }
|
376
|
+
response= execute( o_class, transaction: false) do
|
377
|
+
#[ { type: "cmd", language: 'sql', command: CGI.escapeHTML("create edge #{class_name(o_class)} from #{translate_to_rid[m]} to #{to.to_orient}; ")} ]
|
378
|
+
attr_string = attributes.blank? ? "" : "set #{ generate_sql_list attributes.to_orient }"
|
379
|
+
[ { type: "cmd", language: 'sql',
|
380
|
+
command: "create edge #{class_name(o_class)} from #{from.to_orient} to #{to.to_orient} #{attr_string}"} ]
|
381
|
+
end
|
382
|
+
if response.is_a?(Array) && response.size == 1
|
383
|
+
response.pop # RETURN_VALUE
|
384
|
+
else
|
385
|
+
response
|
367
386
|
end
|
368
387
|
end
|
369
|
-
logger.debug { "creating edge #{class_name(o_class)} from #{from.to_orient} to #{to.to_orient} " }
|
370
|
-
response= execute( o_class, transaction: false) do
|
371
|
-
#[ { type: "cmd", language: 'sql', command: CGI.escapeHTML("create edge #{class_name(o_class)} from #{translate_to_rid[m]} to #{to.to_roient}; ")} ]
|
372
|
-
attr_string = attributes.blank? ? "" : "set #{ generate_sql_list attributes.to_orient }"
|
373
|
-
[ { type: "cmd", language: 'sql',
|
374
|
-
command: "create edge #{class_name(o_class)} from #{from.to_orient} to #{to.to_orient} #{attr_string}"} ]
|
375
|
-
end
|
376
|
-
if response.is_a?(Array) && response.size == 1
|
377
|
-
response.pop # RETURN_VALUE
|
378
|
-
else
|
379
|
-
response
|
380
|
-
end
|
381
388
|
end
|
382
389
|
|
383
390
|
=begin
|
@@ -440,15 +447,49 @@ todo: remove all instances of the class
|
|
440
447
|
end
|
441
448
|
|
442
449
|
end
|
450
|
+
=begin
|
451
|
+
create a single property on class-level.
|
443
452
|
|
444
|
-
|
453
|
+
supported types: https://orientdb.com/docs/last/SQL-Create-Property.html
|
454
|
+
|
455
|
+
if index is to be specified, it's defined in the optional block
|
456
|
+
create_property(class, field){ :unique | :notunique } --> creates an automatic-Index on the given field
|
457
|
+
create_property( class, field){ { »name« => :unique | :notunique | :full_text } } --> creates a manual index
|
458
|
+
|
459
|
+
=end
|
460
|
+
def create_property o_class, field, index: nil, **args
|
445
461
|
logger.progname= 'OrientDB#CreateProperty'
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
462
|
+
c= create_properties o_class, {field => args} # translate_property_hash( field, args )
|
463
|
+
if index.nil? && block_given?
|
464
|
+
index = yield
|
465
|
+
end
|
466
|
+
if c==1 && index.present?
|
467
|
+
if index.is_a?( String ) || index.is_a?( Symbol )
|
468
|
+
create_index o_class, name: field, type: index
|
469
|
+
elsif index.is_a? Hash
|
470
|
+
bez= index.keys.first
|
471
|
+
# puts "index: #{index.inspect}"
|
472
|
+
# puts "bez: #{bez} ---> #{index.keys.inspect}"
|
473
|
+
create_index o_class, name: bez, type: index[bez], on: [ field ]
|
474
|
+
end
|
475
|
+
end
|
476
|
+
|
477
|
+
end
|
478
|
+
|
479
|
+
|
480
|
+
def create_index o_class , name:, on: :automatic, type: :unique
|
481
|
+
|
482
|
+
execute transaction: false do
|
483
|
+
c = class_name o_class
|
484
|
+
command = if on == :automatic
|
485
|
+
"create index #{c}.#{name} #{type.to_s.upcase}"
|
486
|
+
elsif on.is_a? Array
|
487
|
+
"create index #{name} on #{class_name(o_class)}( #{on.join(', ')}) #{type.to_s.upcase}"
|
488
|
+
else
|
489
|
+
nil
|
490
|
+
end
|
491
|
+
[ { type: "cmd", language: 'sql', command: command } ]
|
492
|
+
end
|
452
493
|
|
453
494
|
end
|
454
495
|
## -----------------------------------------------------------------------------------------
|
@@ -458,23 +499,54 @@ todo: remove all instances of the class
|
|
458
499
|
## create_properties
|
459
500
|
## get_class_properties
|
460
501
|
## delete_properties
|
502
|
+
|
461
503
|
##
|
462
504
|
## -----------------------------------------------------------------------------------------
|
505
|
+
private
|
506
|
+
def translate_property_hash field, type: nil, linked_class: nil, **args
|
507
|
+
type = type.presence || args[:propertyType].presence || args[:property_type]
|
508
|
+
linked_class = linked_class.presence || args[:linkedClass]
|
509
|
+
if type.present?
|
510
|
+
if linked_class.nil?
|
511
|
+
{ field => { propertyType: type.to_s.upcase } }
|
512
|
+
else
|
513
|
+
{ field => { propertyType: type.to_s.upcase, linkedClass: class_name( linked_class ) } }
|
514
|
+
end
|
515
|
+
end
|
516
|
+
|
517
|
+
end
|
518
|
+
public
|
463
519
|
=begin
|
464
520
|
|
465
|
-
creates properties
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
521
|
+
creates properties and optional an associated index as defined in the provided block
|
522
|
+
|
523
|
+
create_properties( classname or class, properties as hash ) { index }
|
524
|
+
|
525
|
+
The default-case
|
526
|
+
|
527
|
+
create_properties( con_id: { type: :integer },
|
528
|
+
details: { type: :link, linked_class: 'Contracts' } ) do
|
529
|
+
contract_idx: :notunique
|
530
|
+
end
|
531
|
+
|
532
|
+
A composite index
|
533
|
+
|
534
|
+
create_properties( con_id: { type: :integer },
|
535
|
+
symbol: { type: :string } ) do
|
536
|
+
{ name: 'indexname',
|
537
|
+
on: [ :con_id , :details ] # default: all specified properties
|
538
|
+
type: :notunique # default: :unique
|
470
539
|
}
|
540
|
+
end
|
471
541
|
|
472
542
|
=end
|
473
|
-
def create_properties o_class
|
543
|
+
def create_properties o_class, all_properties, &b
|
544
|
+
|
545
|
+
all_properties_in_a_hash = HashWithIndifferentAccess.new
|
546
|
+
all_properties.each{| field, args | all_properties_in_a_hash.merge! translate_property_hash( field, args ) }
|
474
547
|
|
475
548
|
begin
|
476
|
-
all_properties_in_a_hash
|
477
|
-
if all_properties_in_a_hash.is_a? Hash
|
549
|
+
count = if all_properties_in_a_hash.is_a?( Hash )
|
478
550
|
response = @res[ property_uri(class_name(o_class)) ].post all_properties_in_a_hash.to_json
|
479
551
|
if response.code == 201
|
480
552
|
response.body.to_i
|
@@ -486,12 +558,24 @@ creates properties which are defined as json in the provided block as
|
|
486
558
|
response = JSON.parse( e.response)['errors'].pop
|
487
559
|
error_message = response['content'].split(':').last
|
488
560
|
# if error_message ~= /Missing linked class/
|
489
|
-
logger.progname= 'OrientDB#
|
561
|
+
logger.progname= 'OrientDB#CreatePropertes'
|
490
562
|
logger.error { "Properties in #{class_name(o_class)} were NOT created" }
|
491
563
|
logger.error { "Error-code #{response['code']} --> #{response['content'].split(':').last }" }
|
492
564
|
nil
|
493
565
|
end
|
494
|
-
|
566
|
+
### index
|
567
|
+
if block_given? && count == all_properties_in_a_hash.size
|
568
|
+
index = yield
|
569
|
+
if index.is_a?( Hash )
|
570
|
+
if index.size == 1
|
571
|
+
create_index o_class, name: index.keys.first, on: all_properties_in_a_hash.keys, type: index.values.first
|
572
|
+
else
|
573
|
+
index_hash = HashWithIndifferentAccess.new( type: :unique, on: all_properties_in_a_hash.keys ).merge index
|
574
|
+
create_index o_class, index_hash # i [:name], on: index_hash[:on], type: index_hash[:type]
|
575
|
+
end
|
576
|
+
end
|
577
|
+
end
|
578
|
+
count # return_value
|
495
579
|
end
|
496
580
|
|
497
581
|
def delete_property o_class, field
|
@@ -562,19 +646,22 @@ end
|
|
562
646
|
Creates an Object in the Database and returns this as ActuveOrient::Model-Instance
|
563
647
|
=end
|
564
648
|
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
649
|
+
def create_document o_class, attributes: {}
|
650
|
+
attributes = yield if attributes.empty? && block_given?
|
651
|
+
post_argument = { '@class' => class_name(o_class) }.merge(attributes).to_orient
|
652
|
+
|
653
|
+
begin
|
654
|
+
logger.progname = 'OrientDB#CreateDocument'
|
655
|
+
response = @res[ document_uri ].post post_argument.to_json
|
656
|
+
data= JSON.parse( response.body )
|
657
|
+
ActiveOrient::Model.orientdb_class( name: data['@class']).new data
|
658
|
+
rescue RestClient::InternalServerError => e
|
659
|
+
response = JSON.parse( e.response)['errors'].pop
|
660
|
+
logger.error { response['content'].split(':')[1..-1].join(':') }
|
661
|
+
logger.error { "No Object allocated" }
|
662
|
+
nil # return_value
|
663
|
+
end
|
664
|
+
end
|
578
665
|
|
579
666
|
|
580
667
|
def delete_document record_id
|
@@ -633,7 +720,7 @@ end
|
|
633
720
|
logger.progname = 'OrientDB#count_documents'
|
634
721
|
|
635
722
|
query = OrientSupport::OrientQuery.new args
|
636
|
-
query.projection 'COUNT (*)'
|
723
|
+
query.projection << 'COUNT (*)'
|
637
724
|
result = get_documents raw: true, query: query
|
638
725
|
|
639
726
|
result.first['COUNT']
|
@@ -824,16 +911,16 @@ structure of the provided block:
|
|
824
911
|
def execute classname = 'Myquery', transaction: true
|
825
912
|
batch = { transaction: transaction, operations: yield }
|
826
913
|
unless batch[:operations].blank?
|
827
|
-
#
|
828
|
-
# puts "post: #{batch.to_json}"
|
914
|
+
# puts "post: #{batch.to_json}"
|
829
915
|
response = @res[ batch_uri ].post batch.to_json
|
916
|
+
# puts "response: #{JSON.parse(response.body)['result'].inspect}"
|
830
917
|
if response.code == 200
|
831
918
|
if response.body['result'].present?
|
832
919
|
result= JSON.parse(response.body)['result']
|
833
920
|
result.map do |x|
|
834
921
|
if x.is_a? Hash
|
835
922
|
if x.has_key?('@class')
|
836
|
-
|
923
|
+
ActiveOrient::Model.orientdb_class( name: x['@class']).new x
|
837
924
|
elsif x.has_key?( 'value' )
|
838
925
|
x['value']
|
839
926
|
else
|
data/lib/support.rb
CHANGED
@@ -20,6 +20,8 @@ class String
|
|
20
20
|
self
|
21
21
|
end
|
22
22
|
end
|
23
|
+
## this enables the universal use of an rid-string as pseudonym for a ActiveOrient::Model
|
24
|
+
alias :reload! from_orient
|
23
25
|
end
|
24
26
|
class NilClass
|
25
27
|
def to_orient
|
@@ -152,9 +154,23 @@ Used by update and select
|
|
152
154
|
|
153
155
|
def initialize **args
|
154
156
|
@projection = []
|
155
|
-
@misc= []
|
157
|
+
@misc = []
|
158
|
+
@let = []
|
159
|
+
@where = []
|
160
|
+
@order = []
|
156
161
|
args.each do | k,v|
|
157
|
-
|
162
|
+
case k
|
163
|
+
when :projection
|
164
|
+
@projection << v
|
165
|
+
when :let
|
166
|
+
@let << v
|
167
|
+
when :order
|
168
|
+
@order << v
|
169
|
+
when :where
|
170
|
+
@where << v
|
171
|
+
else
|
172
|
+
self.send k, v
|
173
|
+
end
|
158
174
|
end
|
159
175
|
end
|
160
176
|
|
@@ -171,8 +187,10 @@ Used by update and select
|
|
171
187
|
end
|
172
188
|
|
173
189
|
def compose
|
174
|
-
[ "select",
|
190
|
+
[ "select", projection_s, from, let_s, where_s , subquery, misc, order_s , group_by, unwind, skip ].compact.join(' ')
|
175
191
|
end
|
192
|
+
|
193
|
+
alias :to_s :compose
|
176
194
|
=begin
|
177
195
|
from can either be a Databaseclass to operate on or a Subquery providing data to query further
|
178
196
|
=end
|
@@ -188,17 +206,14 @@ from can either be a Databaseclass to operate on or a Subquery providing data to
|
|
188
206
|
when Symbol
|
189
207
|
arg
|
190
208
|
when OrientQuery
|
191
|
-
|
209
|
+
arg
|
192
210
|
end
|
193
|
-
|
211
|
+
compose # return the complete query
|
194
212
|
else # read from
|
195
|
-
"from " <<
|
196
|
-
"( #{@from.compose} )"
|
197
|
-
else
|
198
|
-
@database.to_s
|
213
|
+
"from " << @database.to_s unless @database.nil?
|
199
214
|
end
|
200
215
|
end
|
201
|
-
|
216
|
+
alias :from= :from
|
202
217
|
def database_class= arg
|
203
218
|
@database = arg if @database.present?
|
204
219
|
if @from.is_a? OrientQuery
|
@@ -225,23 +240,32 @@ where: "r > 9" --> where r > 9
|
|
225
240
|
where: {a: 9, b: 's'} --> where a = 9 and b = 's'
|
226
241
|
where:[{ a: 2} , 'b > 3',{ c: 'ufz' }] --> where a = 2 and b > 3 and c = 'ufz'
|
227
242
|
=end
|
228
|
-
|
229
|
-
|
230
|
-
@where
|
243
|
+
attr_accessor :where
|
244
|
+
def where_s
|
245
|
+
compose_where @where
|
231
246
|
end
|
232
247
|
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
248
|
+
|
249
|
+
attr_accessor :let
|
250
|
+
def let_s
|
251
|
+
unless @let.empty?
|
252
|
+
"let " << @let.map do |s|
|
253
|
+
case s
|
254
|
+
when Hash
|
255
|
+
s.map{ |x,y| "$#{x} = ( #{y} )"}.join( ', ')
|
256
|
+
when Array
|
257
|
+
s.join(', ')
|
258
|
+
else
|
259
|
+
s
|
260
|
+
end
|
261
|
+
end.join(', ')
|
262
|
+
end
|
239
263
|
end
|
240
|
-
|
241
|
-
|
264
|
+
|
265
|
+
def distinct d
|
242
266
|
@projection << case d
|
243
|
-
when String
|
244
|
-
"distinct( #{d} )"
|
267
|
+
when String, Symbol
|
268
|
+
"distinct( #{d.to_s} )"
|
245
269
|
when Array
|
246
270
|
"distinct( #{d.first} ) as #{d.last}"
|
247
271
|
when Hash
|
@@ -249,13 +273,15 @@ where:[{ a: 2} , 'b > 3',{ c: 'ufz' }] --> where a = 2 and b > 3 and c = 'ufz'
|
|
249
273
|
else
|
250
274
|
""
|
251
275
|
end
|
252
|
-
|
253
|
-
@projection.join(' ,') #return_value
|
276
|
+
compose # return the hole query
|
254
277
|
end
|
255
|
-
|
256
|
-
|
278
|
+
alias :distinct= :distinct
|
279
|
+
|
280
|
+
attr_accessor :projection
|
281
|
+
def projection_s
|
257
282
|
|
258
|
-
@projection
|
283
|
+
@projection.map do | s |
|
284
|
+
case s
|
259
285
|
when Hash
|
260
286
|
s.map{ |x,y| "#{x} as #{y}"}.join( ', ')
|
261
287
|
when Array
|
@@ -263,11 +289,11 @@ where:[{ a: 2} , 'b > 3',{ c: 'ufz' }] --> where a = 2 and b > 3 and c = 'ufz'
|
|
263
289
|
else
|
264
290
|
s
|
265
291
|
end
|
292
|
+
end.join( ', ' )
|
266
293
|
|
267
|
-
end
|
268
|
-
@projection.join(', ')
|
269
294
|
end
|
270
295
|
|
296
|
+
|
271
297
|
# def where= w
|
272
298
|
|
273
299
|
# end
|
@@ -286,19 +312,25 @@ where:[{ a: 2} , 'b > 3',{ c: 'ufz' }] --> where a = 2 and b > 3 and c = 'ufz'
|
|
286
312
|
|
287
313
|
def skip n=nil
|
288
314
|
@skip= n if n.present?
|
289
|
-
"skip #{
|
315
|
+
"skip #{@skip}" if @skip.present?
|
290
316
|
end
|
291
317
|
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
318
|
+
attr_accessor :order
|
319
|
+
|
320
|
+
def order_s
|
321
|
+
unless @order.empty?
|
322
|
+
# the [@order] is nessesary to enable query.order= "..." oder query.order= { a: :b }
|
323
|
+
"order by " << [@order].flatten.map do | o |
|
324
|
+
case o
|
325
|
+
when Hash
|
326
|
+
o.map{ |x,y| "#{x} #{y}" }.join( " " )
|
327
|
+
else
|
328
|
+
o.to_s
|
329
|
+
end # case
|
330
|
+
end.join(', ')
|
298
331
|
else
|
299
|
-
|
300
|
-
end
|
301
|
-
@order_string
|
332
|
+
''
|
333
|
+
end
|
302
334
|
end
|
303
335
|
# misc_string = if skip > 0 && limit > 0
|
304
336
|
# " skip: #{skip} "
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active-orient
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.4'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Hartmut Bischoff
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-09-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -58,14 +58,14 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 2.0
|
61
|
+
version: '2.0'
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: 2.0
|
68
|
+
version: '2.0'
|
69
69
|
description: Persistent ORM for OrientDB, based on ActiveModel
|
70
70
|
email:
|
71
71
|
- topofocus@gmail.com
|
@@ -83,6 +83,8 @@ files:
|
|
83
83
|
- active-orient.gemspec
|
84
84
|
- config/boot.rb
|
85
85
|
- config/connect.yml
|
86
|
+
- examples/books.rb
|
87
|
+
- examples/streets.rb
|
86
88
|
- lib/active-orient.rb
|
87
89
|
- lib/base.rb
|
88
90
|
- lib/base_properties.rb
|