arcadedb 0.3.1 → 0.3.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +57 -0
- data/CHANGELOG.md +15 -0
- data/Gemfile +30 -0
- data/Gemfile.lock +168 -0
- data/Guardfile +30 -0
- data/LICENSE +21 -0
- data/README.md +242 -0
- data/Rakefile +11 -0
- data/arcade.yml +27 -0
- data/arcadedb.gemspec +35 -0
- data/bin/+ +106 -0
- data/bin/console +126 -0
- data/examples/books.rb +139 -0
- data/examples/relation_1__1.rb +149 -0
- data/examples/relation_1__n.rb +56 -0
- data/examples/relation_n_n.rb +194 -0
- data/lib/arcade/api/operations.rb +306 -0
- data/lib/arcade/api/version.rb +5 -0
- data/lib/arcade/base.rb +455 -0
- data/lib/arcade/database.rb +338 -0
- data/lib/arcade/errors.rb +71 -0
- data/lib/arcade/logging.rb +38 -0
- data/lib/arcade/version.rb +3 -0
- data/lib/arcade.rb +39 -0
- data/lib/config.rb +70 -0
- data/lib/init.rb +50 -0
- data/lib/match.rb +216 -0
- data/lib/model/basicdocument.rb +7 -0
- data/lib/model/basicedge.rb +6 -0
- data/lib/model/basicvertex.rb +6 -0
- data/lib/model/document.rb +10 -0
- data/lib/model/edge.rb +47 -0
- data/lib/model/vertex.rb +238 -0
- data/lib/models.rb +6 -0
- data/lib/query.rb +384 -0
- data/lib/support/class.rb +13 -0
- data/lib/support/conversions.rb +295 -0
- data/lib/support/model.rb +84 -0
- data/lib/support/object.rb +20 -0
- data/lib/support/sql.rb +74 -0
- data/lib/support/string.rb +116 -0
- data/rails/arcade.rb +20 -0
- data/rails/config.yml +10 -0
- data/rails/connect.yml +17 -0
- data/rails.md +147 -0
- metadata +49 -4
@@ -0,0 +1,194 @@
|
|
1
|
+
##
|
2
|
+
## This example realises a bidirectional 1:n relation using Edges & Vertices
|
3
|
+
#
|
4
|
+
## The schema is implemented in modelfiles located in spec/model
|
5
|
+
## /spec/models/ex/human.rb # Vertex
|
6
|
+
## /spec/models/ex/depend_on.rb # Edge
|
7
|
+
#
|
8
|
+
# This script runs in the test environment.
|
9
|
+
##
|
10
|
+
require 'bundler/setup'
|
11
|
+
require 'zeitwerk'
|
12
|
+
require 'arcade'
|
13
|
+
|
14
|
+
include Arcade
|
15
|
+
## require modelfiles
|
16
|
+
loader = Zeitwerk::Loader.new
|
17
|
+
loader.push_dir ("#{__dir__}/../spec/model")
|
18
|
+
loader.setup
|
19
|
+
|
20
|
+
## clear test database
|
21
|
+
|
22
|
+
databases = Arcade::Api.databases
|
23
|
+
if databases.include?(Arcade::Config.database[:test])
|
24
|
+
Arcade::Api.drop_database Arcade::Config.database[:test]
|
25
|
+
end
|
26
|
+
Arcade::Api.create_database Arcade::Config.database[:test]
|
27
|
+
|
28
|
+
## Universal Database handle
|
29
|
+
DB = Arcade::Init.connect 'test'
|
30
|
+
|
31
|
+
## ------------------------------------------------------ End Setup ------------------------------------- ##
|
32
|
+
##
|
33
|
+
## We are realising a self referencing relation
|
34
|
+
## parent <--> children
|
35
|
+
#
|
36
|
+
|
37
|
+
|
38
|
+
Ex::Human.create_type # initialize the database
|
39
|
+
Ex::DependOn.create_type
|
40
|
+
|
41
|
+
nodes = %w( Guthorn Fulkerson Sniezek Tomasulo Portwine Keala Revelli Jacks Gorby Alcaoa ).map do | name |
|
42
|
+
Ex::Human.insert name: name, birth: 2022 - rand(99), married: rand(2)==1
|
43
|
+
end
|
44
|
+
|
45
|
+
puts Ex::Human.count.to_s + " Human Vertices created"
|
46
|
+
|
47
|
+
puts "------------------------------ get a sorted list of married humans ---------------------------------"
|
48
|
+
puts
|
49
|
+
|
50
|
+
merried = Ex::Human.query( where: { married: true })
|
51
|
+
merried.order 'birth'
|
52
|
+
new_merried = Query.new projection: 'name, 2022-birth as age ', from: merried # merge two queries
|
53
|
+
puts new_merried.query
|
54
|
+
|
55
|
+
puts "------------------------------ and one for not married humans ---------------------------------"
|
56
|
+
puts
|
57
|
+
|
58
|
+
singles = Ex::Human.query( where: { married: false })
|
59
|
+
singles.order 'birth'
|
60
|
+
new_singles = Query.new projection: 'name, 2022-birth as age ', from: singles # merge two queries
|
61
|
+
puts new_singles.query
|
62
|
+
|
63
|
+
|
64
|
+
puts "------------------------------ connect married humans with children ------------------------------"
|
65
|
+
children = singles.query.allocate_model
|
66
|
+
|
67
|
+
begin
|
68
|
+
children_enumerator = children.each
|
69
|
+
merried.query.allocate_model.map do | parent |
|
70
|
+
parent.assign via: Ex::DependOn, vertex: children_enumerator.next
|
71
|
+
end
|
72
|
+
rescue StopIteration
|
73
|
+
puts "No more children"
|
74
|
+
end
|
75
|
+
|
76
|
+
# Ex::Human.parents is essential
|
77
|
+
# EX::Human.query projection: 'out()' , whee: { married: true }
|
78
|
+
# Ex::Human.children is essential
|
79
|
+
# EX::Human.query projection: 'out()' , whee: { married: true }
|
80
|
+
puts "--------------------------- Parent and Children ---------------------------------------------------"
|
81
|
+
puts
|
82
|
+
puts "%10s %7s %10s %30s " % ["Parent", "Age", "Child", "sorted by Child"]
|
83
|
+
puts "- " * 50
|
84
|
+
Ex::Human.parents( order: 'name' ).each do |parent| # note: order: 'name' is included
|
85
|
+
# in the query
|
86
|
+
puts "%10s %7d %10s " % [parent.name, 2022 - parent.birth, parent.out.first.name]
|
87
|
+
end
|
88
|
+
|
89
|
+
puts "--------------------------- child and parent -----------------------------------------------------"
|
90
|
+
puts
|
91
|
+
puts "%10s %7s %10s %30s " % ["Child", "Age", "Parent", "sorted by Parent"]
|
92
|
+
puts "- " * 50
|
93
|
+
Ex::Human.children( order: 'name' ).each do |child|
|
94
|
+
puts "%10s %7d %10s " % [child.name, 2022 - child.birth, child.in.first.name]
|
95
|
+
end
|
96
|
+
|
97
|
+
puts "--------------------------- Add child to a parent -----------------------------------------------"
|
98
|
+
puts
|
99
|
+
|
100
|
+
Ex::Human.parents.first.assign via: Ex::DependOn, vertex: Ex::Human.insert( name: "TestBaby", birth: 2022, married: false)
|
101
|
+
|
102
|
+
puts "Parent: " + Ex::Human.parents.first.to_human
|
103
|
+
puts "Children: \n " + Ex::Human.parents.first.out.to_human.join("\n ")
|
104
|
+
|
105
|
+
## Expected output
|
106
|
+
__END__
|
107
|
+
|
108
|
+
Using default database credentials and settings fron /home/ubuntu/workspace/arcadedb
|
109
|
+
27.04.(18:35:05) INFO->Q: create vertex type ex_human
|
110
|
+
27.04.(18:35:05) INFO->Q: CREATE PROPERTY ex_human.name STRING
|
111
|
+
27.04.(18:35:05) INFO->Q: CREATE PROPERTY ex_human.married BOOLEAN
|
112
|
+
27.04.(18:35:05) INFO->Q: CREATE INDEX `Example[human]` ON ex_human ( name ) UNIQUE
|
113
|
+
27.04.(18:35:05) INFO->Q: create edge type ex_depend_on
|
114
|
+
27.04.(18:35:05) INFO->Q: CREATE INDEX depends_out_in ON ex_depend_on (`@out`, `@in`) UNIQUE
|
115
|
+
27.04.(18:35:05) INFO->Q: INSERT INTO ex_human CONTENT {"name":"Guthorn","birth":1962,"married":false}
|
116
|
+
27.04.(18:35:05) INFO->Q: INSERT INTO ex_human CONTENT {"name":"Fulkerson","birth":1962,"married":true}
|
117
|
+
27.04.(18:35:05) INFO->Q: INSERT INTO ex_human CONTENT {"name":"Sniezek","birth":1972,"married":true}
|
118
|
+
27.04.(18:35:05) INFO->Q: INSERT INTO ex_human CONTENT {"name":"Tomasulo","birth":1953,"married":false}
|
119
|
+
27.04.(18:35:05) INFO->Q: INSERT INTO ex_human CONTENT {"name":"Portwine","birth":1975,"married":true}
|
120
|
+
27.04.(18:35:05) INFO->Q: INSERT INTO ex_human CONTENT {"name":"Keala","birth":1961,"married":false}
|
121
|
+
27.04.(18:35:05) INFO->Q: INSERT INTO ex_human CONTENT {"name":"Revelli","birth":1948,"married":true}
|
122
|
+
27.04.(18:35:05) INFO->Q: INSERT INTO ex_human CONTENT {"name":"Jacks","birth":1993,"married":true}
|
123
|
+
27.04.(18:35:05) INFO->Q: INSERT INTO ex_human CONTENT {"name":"Gorby","birth":1979,"married":false}
|
124
|
+
27.04.(18:35:05) INFO->Q: INSERT INTO ex_human CONTENT {"name":"Alcaoa","birth":1960,"married":false}
|
125
|
+
27.04.(18:35:05) INFO->Q: select count(*) from ex_human
|
126
|
+
10 Human Vertices created
|
127
|
+
------------------------------ get a sorted list of married humans ---------------------------------
|
128
|
+
|
129
|
+
27.04.(18:35:05) INFO->Q: select name, 2022-birth as age from ( select from ex_human where married = true order by birth )
|
130
|
+
{:name=>"Revelli", :age=>74}
|
131
|
+
{:name=>"Fulkerson", :age=>60}
|
132
|
+
{:name=>"Sniezek", :age=>50}
|
133
|
+
{:name=>"Portwine", :age=>47}
|
134
|
+
{:name=>"Jacks", :age=>29}
|
135
|
+
------------------------------ and one for not married humans ---------------------------------
|
136
|
+
|
137
|
+
27.04.(18:35:05) INFO->Q: select name, 2022-birth as age from ( select from ex_human where married = false order by birth )
|
138
|
+
{:name=>"Tomasulo", :age=>69}
|
139
|
+
{:name=>"Alcaoa", :age=>62}
|
140
|
+
{:name=>"Keala", :age=>61}
|
141
|
+
{:name=>"Guthorn", :age=>60}
|
142
|
+
{:name=>"Gorby", :age=>43}
|
143
|
+
------------------------------ connect married humans with children ------------------------------
|
144
|
+
27.04.(18:35:05) INFO->Q: select from ex_human where married = false order by birth
|
145
|
+
27.04.(18:35:05) INFO->Q: select from ex_human where married = true order by birth
|
146
|
+
27.04.(18:35:05) INFO->Q: create edge ex_depend_on from #19:0 to #10:0 CONTENT {"set":{}}
|
147
|
+
27.04.(18:35:05) INFO->Q: create edge ex_depend_on from #4:0 to #4:1 CONTENT {"set":{}}
|
148
|
+
27.04.(18:35:05) INFO->Q: create edge ex_depend_on from #7:0 to #16:0 CONTENT {"set":{}}
|
149
|
+
27.04.(18:35:05) INFO->Q: create edge ex_depend_on from #13:0 to #1:0 CONTENT {"set":{}}
|
150
|
+
27.04.(18:35:05) INFO->Q: create edge ex_depend_on from #22:0 to #1:1 CONTENT {"set":{}}
|
151
|
+
--------------------------- Parent and Children ---------------------------------------------------
|
152
|
+
|
153
|
+
Parent Age Child sorted by Child
|
154
|
+
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
155
|
+
27.04.(18:35:05) INFO->Q: select in() from ex_human where married = false order by name
|
156
|
+
27.04.(18:35:05) INFO->Q: select out() from #4:0
|
157
|
+
Fulkerson 60 Alcaoa
|
158
|
+
27.04.(18:35:05) INFO->Q: select out() from #22:0
|
159
|
+
Jacks 29 Gorby
|
160
|
+
27.04.(18:35:05) INFO->Q: select out() from #13:0
|
161
|
+
Portwine 47 Guthorn
|
162
|
+
27.04.(18:35:05) INFO->Q: select out() from #7:0
|
163
|
+
Sniezek 50 Keala
|
164
|
+
27.04.(18:35:05) INFO->Q: select out() from #19:0
|
165
|
+
Revelli 74 Tomasulo
|
166
|
+
--------------------------- child and parent -----------------------------------------------------
|
167
|
+
|
168
|
+
Child Age Parent sorted by Parent
|
169
|
+
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
170
|
+
27.04.(18:35:05) INFO->Q: select out() from ex_human where married = true order by name
|
171
|
+
27.04.(18:35:05) INFO->Q: select in() from #4:1
|
172
|
+
Alcaoa 62 Fulkerson
|
173
|
+
27.04.(18:35:05) INFO->Q: select in() from #1:1
|
174
|
+
Gorby 43 Jacks
|
175
|
+
27.04.(18:35:05) INFO->Q: select in() from #1:0
|
176
|
+
Guthorn 60 Portwine
|
177
|
+
27.04.(18:35:05) INFO->Q: select in() from #10:0
|
178
|
+
Tomasulo 69 Revelli
|
179
|
+
27.04.(18:35:05) INFO->Q: select in() from #16:0
|
180
|
+
Keala 61 Sniezek
|
181
|
+
--------------------------- Add child to a parent -----------------------------------------------
|
182
|
+
|
183
|
+
27.04.(18:35:05) INFO->Q: select in() from ex_human where married = false
|
184
|
+
27.04.(18:35:05) INFO->Q: INSERT INTO ex_human CONTENT {"name":"TestBaby","birth":2022,"married":false}
|
185
|
+
27.04.(18:35:05) INFO->Q: create edge ex_depend_on from #13:0 to #7:1 CONTENT {"set":{}}
|
186
|
+
27.04.(18:35:05) INFO->Q: select in() from ex_human where married = false
|
187
|
+
Parent: <ex_human[#13:0]: {0->}{->2}}, birth: 1975, married: true, name: Portwine>
|
188
|
+
27.04.(18:35:05) INFO->Q: select in() from ex_human where married = false
|
189
|
+
27.04.(18:35:05) INFO->Q: select out() from #13:0
|
190
|
+
Children:
|
191
|
+
<ex_human[#1:0]: {->}{->}}, birth: 1962, married: false, name: Guthorn>
|
192
|
+
<ex_human[#7:1]: {->}{->}}, birth: 2022, married: false, name: TestBaby>
|
193
|
+
|
194
|
+
|
@@ -0,0 +1,306 @@
|
|
1
|
+
module Arcade
|
2
|
+
module Api
|
3
|
+
=begin
|
4
|
+
This is a simple admin interface
|
5
|
+
|
6
|
+
$ Arcade::Api.databases # returns an Array of known databases
|
7
|
+
$ Arcade::Api.create_database <a string> # returns true if succesfull
|
8
|
+
$ Arcade::Api.drop_database <a string> # returns true if successfull
|
9
|
+
|
10
|
+
$ Arcade::Api.create_document <database>, <type>, attributes
|
11
|
+
$ Arcade::Api.execute( <database> ) { <query> }
|
12
|
+
$ Arcade::Api.query( <database> ) { <query> }
|
13
|
+
$ Arcade::Api.get_record <database>, rid # returns a hash
|
14
|
+
|
15
|
+
|
16
|
+
<query> is either a string
|
17
|
+
or a hash { :query => " ",
|
18
|
+
:language => one of :sql, :cypher, :gmelion: :neo4j ,
|
19
|
+
:params => a hash of parameters,
|
20
|
+
:limit => a number ,
|
21
|
+
:serializer: one of :graph, :record }
|
22
|
+
|
23
|
+
=end
|
24
|
+
|
25
|
+
|
26
|
+
def self.databases
|
27
|
+
get_data 'databases'
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.create_database name
|
31
|
+
unless databases.include?( name.to_s )
|
32
|
+
payload = { "command" => "create database #{name}" }.to_json
|
33
|
+
post_data "server", { body: payload }.merge( auth ).merge( json )
|
34
|
+
end
|
35
|
+
rescue QueryError => e
|
36
|
+
logger.fatal "Create database #{name} through \"POST create/#{name}\" failed"
|
37
|
+
logger.fatal e
|
38
|
+
raise
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.drop_database name
|
42
|
+
if databases.include?( name.to_s )
|
43
|
+
payload = { "command" => "drop database #{name}" }.to_json
|
44
|
+
post_data "server", { body: payload }.merge( auth ).merge( json )
|
45
|
+
end
|
46
|
+
end
|
47
|
+
# ------------------------------ create document ------------------------------------------------- #
|
48
|
+
# adds a document to the database
|
49
|
+
#
|
50
|
+
# specify database-fields as hash-type parameters
|
51
|
+
#
|
52
|
+
# i.e Arcade::Api.create_document 'devel', 'documents', name: 'herta meyer', age: 56, sex: 'f'
|
53
|
+
#
|
54
|
+
# returns the rid of the inserted dataset
|
55
|
+
#
|
56
|
+
def self.create_document database, type, **attributes
|
57
|
+
payload = { "@type" => type }.merge( attributes ).to_json
|
58
|
+
logger.debug "C: #{payload}"
|
59
|
+
options = if session.nil?
|
60
|
+
{ body: payload }.merge( auth ).merge( json )
|
61
|
+
else
|
62
|
+
{ body: payload }.merge( auth ).merge( json ).merge( headers: { "arcadedb-session-id" => session })
|
63
|
+
end
|
64
|
+
post_data "document/#{database}", options
|
65
|
+
end
|
66
|
+
|
67
|
+
# ------------------------------ execute ------------------------------------------------- #
|
68
|
+
# executes a sql-query in the specified database
|
69
|
+
#
|
70
|
+
# the query is provided as block
|
71
|
+
#
|
72
|
+
# returns an Array of results (if propriate)
|
73
|
+
# i.e
|
74
|
+
# Arcade::Api.execcute( "devel" ) { 'select from test ' }
|
75
|
+
# =y [{"@rid"=>"#57:0", "@type"=>"test", "name"=>"Hugo"}, {"@rid"=>"#60:0", "@type"=>"test", "name"=>"Hubert"}]
|
76
|
+
#
|
77
|
+
def self.execute database, query=nil
|
78
|
+
pl = query.nil? ? provide_payload(yield) : provide_payload(query)
|
79
|
+
options = { body: pl }.merge( auth ).merge( json )
|
80
|
+
unless session.nil?
|
81
|
+
options = options.merge( headers: { "arcadedb-session-id" => session })
|
82
|
+
end
|
83
|
+
post_data "command/#{database}" , options
|
84
|
+
rescue Arcade::QueryError => e
|
85
|
+
# puts e.methods
|
86
|
+
#puts e.exception
|
87
|
+
# puts e.full_message
|
88
|
+
if e.message =~ /retry/
|
89
|
+
retry
|
90
|
+
else
|
91
|
+
raise e.message
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# ------------------------------ query ------------------------------------------------- #
|
96
|
+
# same for idempotent queries
|
97
|
+
def self.query database, query
|
98
|
+
options = { body: provide_payload(query) }.merge( auth ).merge( json )
|
99
|
+
post_data "query/#{database}" , options
|
100
|
+
end
|
101
|
+
|
102
|
+
# ------------------------------ get_record ------------------------------------------------- #
|
103
|
+
# fetches a record by providing database and rid
|
104
|
+
# and returns the result as hash
|
105
|
+
#
|
106
|
+
# > Api.get_record 'devel', '225:6'
|
107
|
+
# > Api.get_record 'devel', 225, 6
|
108
|
+
# > Api.get_record 'devel', '#225:6'
|
109
|
+
# => {:@out=>0, :@rid=>"#225:6", :@in=>0, :@type=>"my_names", :name=>"Zaber", :@cat=>"v"}
|
110
|
+
|
111
|
+
def self.get_record database, *rid
|
112
|
+
rid = rid.join(':')
|
113
|
+
rid = rid[1..-1] if rid[0]=="#"
|
114
|
+
if rid.rid?
|
115
|
+
get_data "document/#{database}/#{rid}"
|
116
|
+
else
|
117
|
+
raise Arcade::Error "Get requires a rid input"
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# ------------------------------ property ------------------------------------------------- #
|
122
|
+
# Adds properties to the type
|
123
|
+
#
|
124
|
+
# call via
|
125
|
+
# Api.property <database>, <type>, name1: a_format , name2: a_format
|
126
|
+
#
|
127
|
+
# Format is one of
|
128
|
+
# Boolean, Integer, Short, Long, Float, Double, String
|
129
|
+
# Datetime, Binary, Byte, Decimal, Link
|
130
|
+
# Embedded, EmbeddedList, EmbeddedMap
|
131
|
+
#
|
132
|
+
# In case of an Error, anything is rolled back and nil is returned
|
133
|
+
#
|
134
|
+
def self.property database, type, **args
|
135
|
+
|
136
|
+
begin_transaction database
|
137
|
+
success = args.map do | name, format |
|
138
|
+
r= execute(database) {" create property #{type.to_s}.#{name.to_s} #{format.to_s} " } &.first
|
139
|
+
if r.nil?
|
140
|
+
false
|
141
|
+
else
|
142
|
+
r.keys == [ :propertyName, :typeName, :operation ] && r[:operation] == 'create property'
|
143
|
+
end
|
144
|
+
end.uniq
|
145
|
+
if success == [true]
|
146
|
+
commit database
|
147
|
+
true
|
148
|
+
else
|
149
|
+
rollback database
|
150
|
+
end
|
151
|
+
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
# ------------------------------ index ------------------------------------------------- #
|
156
|
+
def self.index database, type, name , *properties
|
157
|
+
properties = properties.map( &:to_s )
|
158
|
+
unique_requested = "unique" if properties.delete("unique")
|
159
|
+
unique_requested = "notunique" if properties.delete("notunique" )
|
160
|
+
automatic = true if
|
161
|
+
properties << name if properties.empty?
|
162
|
+
# puts " create index #{type.to_s}[#{name.to_s}] on #{type} ( #{properties.join(',')} ) #{unique_requested}"
|
163
|
+
# VV 22.10: providing an index-name raises an Error ( Encountered " "(" "( "" at line 1, column 44. Was expecting one of: <EOF> <SCHEMA> ... <NULL_STRATEGY> ... ";" ... "," ... )) )
|
164
|
+
# named indices droped for now
|
165
|
+
success = execute(database) {" create index on #{type} ( #{properties.join(',')} ) #{unique_requested}" } &.first
|
166
|
+
# puts "success: #{success}"
|
167
|
+
success && success.keys == [ :totalIndexed, :name, :operation ] && success[:operation] == 'create index'
|
168
|
+
|
169
|
+
end
|
170
|
+
|
171
|
+
|
172
|
+
# ------------------------------ transaction ------------------------------------------------- #
|
173
|
+
#
|
174
|
+
def self.begin_transaction database
|
175
|
+
result = Typhoeus.post Arcade::Config.base_uri + "begin/#{database}", auth
|
176
|
+
@session_id = result.headers["arcadedb-session-id"]
|
177
|
+
|
178
|
+
# returns the session-id
|
179
|
+
end
|
180
|
+
|
181
|
+
|
182
|
+
# ------------------------------ commit ------------------------------------------------- #
|
183
|
+
def self.commit database
|
184
|
+
options = auth.merge( headers: { "arcadedb-session-id" => session })
|
185
|
+
post_data "commit/#{database}", options
|
186
|
+
@session_id = nil
|
187
|
+
end
|
188
|
+
|
189
|
+
# ------------------------------ rollback ------------------------------------------------- #
|
190
|
+
def self.rollback database, publish_error=true
|
191
|
+
options = auth.merge( headers: { "arcadedb-session-id" => session })
|
192
|
+
post_data "rollback/#{database}", options
|
193
|
+
@session_id = nil
|
194
|
+
raise Arcade::RollbackError "A Transaction has been rolled back" if publish_error
|
195
|
+
end
|
196
|
+
|
197
|
+
private
|
198
|
+
|
199
|
+
def self.logger
|
200
|
+
Database.logger
|
201
|
+
end
|
202
|
+
|
203
|
+
def self.session
|
204
|
+
@session_id
|
205
|
+
end
|
206
|
+
|
207
|
+
def self. provide_payload( the_yield, action: :post )
|
208
|
+
unless the_yield.is_a? Hash
|
209
|
+
logger.info "Q: #{the_yield}"
|
210
|
+
the_yield = { :query => the_yield }
|
211
|
+
end
|
212
|
+
{ language: 'sql' }.merge(
|
213
|
+
the_yield.map do | key, value |
|
214
|
+
case key
|
215
|
+
when :query
|
216
|
+
action == :post ? [ :command, value ] : [ :query, value ]
|
217
|
+
when :limit
|
218
|
+
[ :limit , value ]
|
219
|
+
when :params
|
220
|
+
if value.is_a? Hash
|
221
|
+
[ :params, value ]
|
222
|
+
end
|
223
|
+
# serializer (optional) specify the serializer used for the result:
|
224
|
+
# graph: returns as a graph separating vertices from edges
|
225
|
+
# record: returns everything as records
|
226
|
+
# by default it’s like record but with additional metadata for vertex records,
|
227
|
+
# such as the number of outgoing edges in @out property and total incoming edges
|
228
|
+
# in @in property. This serialzier is used by Studio
|
229
|
+
when :serializer
|
230
|
+
if [:graph, :record].include? value.to_sym
|
231
|
+
[ :serializer, value.to_sym ]
|
232
|
+
end
|
233
|
+
when :language
|
234
|
+
if [:sql, :cypher, :gremlin, :neo4j ].include? value.to_sym
|
235
|
+
[ :language, value.to_sym ]
|
236
|
+
end
|
237
|
+
end # case
|
238
|
+
end .to_h ).to_json # map
|
239
|
+
end
|
240
|
+
|
241
|
+
def self.get_data command, options = auth
|
242
|
+
result = Typhoeus.get Arcade::Config.base_uri + command, options
|
243
|
+
analyse_result(result, command)
|
244
|
+
end
|
245
|
+
|
246
|
+
|
247
|
+
def self.post_data command, options = auth
|
248
|
+
# puts "Post DATA #{command} #{options}" # debug
|
249
|
+
i = 0; a=""
|
250
|
+
loop do
|
251
|
+
result = Typhoeus.post Arcade::Config.base_uri + command, options
|
252
|
+
# Code: 503 – Service Unavailable
|
253
|
+
if result.response_code.to_i == 503 # retry two times
|
254
|
+
i += 1
|
255
|
+
raise Arcade::QueryError, JSON.parse( result.response_body, symbolize_names: true )[:result] if i > 3
|
256
|
+
sleep 0.1
|
257
|
+
else
|
258
|
+
a= analyse_result(result, command )
|
259
|
+
break
|
260
|
+
end
|
261
|
+
end
|
262
|
+
a
|
263
|
+
end
|
264
|
+
|
265
|
+
# returns the json-response
|
266
|
+
def self.analyse_result r, command
|
267
|
+
if r.success?
|
268
|
+
return nil if r.response_code == 204 # no content
|
269
|
+
result = JSON.parse( r.response_body, symbolize_names: true )[:result]
|
270
|
+
if result == [{}]
|
271
|
+
[]
|
272
|
+
else
|
273
|
+
result
|
274
|
+
end
|
275
|
+
elsif r.timed_out?
|
276
|
+
raise Arcade::Error "Timeout Error", caller
|
277
|
+
[]
|
278
|
+
elsif r.response_code > 0
|
279
|
+
logger.error "Execution Failure – Code: #{ r.response_code } – #{r.status_message} "
|
280
|
+
error_message = JSON.parse( r.response_body, symbolize_names: true )
|
281
|
+
logger.error "ErrorMessage: #{ error_message[:detail]} "
|
282
|
+
if error_message[:detail] =~ /Duplicated key/
|
283
|
+
raise Arcade::IndexError, error_message[:detail]
|
284
|
+
else
|
285
|
+
# available fields: :detail, :exception, error
|
286
|
+
puts error_message[:detail]
|
287
|
+
#raise error_message[:detail], caller
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
291
|
+
def self.auth
|
292
|
+
@a ||= { httpauth: :basic,
|
293
|
+
username: Arcade::Config.admin[:user],
|
294
|
+
password: Arcade::Config.admin[:pass] }
|
295
|
+
end
|
296
|
+
|
297
|
+
def self.json
|
298
|
+
{ headers: { "Content-Type" => "application/json"} }
|
299
|
+
end
|
300
|
+
# not tested
|
301
|
+
def self.delete_data command
|
302
|
+
result = Typhoeus.delete Arcade::Config.base_uri + command, auth
|
303
|
+
analyse_result(result, command)
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|