arcadedb 0.3.1 → 0.4
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 +19 -0
- data/Gemfile +25 -0
- data/Gemfile.lock +186 -0
- data/Guardfile +30 -0
- data/LICENSE +21 -0
- data/README.md +242 -0
- data/Rakefile +11 -0
- data/arcade.yml +23 -0
- data/arcadedb.gemspec +32 -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 +257 -0
- data/lib/arcade/api/primitives.rb +98 -0
- data/lib/arcade/base.rb +454 -0
- data/lib/arcade/database.rb +367 -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 +36 -0
- data/lib/config.rb +72 -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 +87 -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 +64 -5
@@ -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,257 @@
|
|
1
|
+
module Arcade
|
2
|
+
module Api
|
3
|
+
extend Primitives
|
4
|
+
=begin
|
5
|
+
This is a simple admin interface
|
6
|
+
|
7
|
+
$ Arcade::Api.databases # returns an Array of known databases
|
8
|
+
$ Arcade::Api.create_database <a string> # returns true if succesfull
|
9
|
+
$ Arcade::Api.drop_database <a string> # returns true if successfull
|
10
|
+
|
11
|
+
$ Arcade::Api.create_document <database>, <type>, attributes
|
12
|
+
$ Arcade::Api.execute( <database> ) { <query> }
|
13
|
+
$ Arcade::Api.query( <database> ) { <query> }
|
14
|
+
$ Arcade::Api.get_record <database>, rid # returns a hash
|
15
|
+
|
16
|
+
|
17
|
+
<query> is either a string
|
18
|
+
or a hash { :query => " ",
|
19
|
+
:language => one of :sql, :cypher, :gmelion: :neo4j ,
|
20
|
+
:params => a hash of parameters,
|
21
|
+
:limit => a number ,
|
22
|
+
:serializer: one of :graph, :record }
|
23
|
+
|
24
|
+
=end
|
25
|
+
|
26
|
+
# ------------------------------ Service methods ------------------------------------------------- #
|
27
|
+
# ------------------------------ ------------------------------------------------- #
|
28
|
+
# ------------------------------ databases ------------------------------------------------- #
|
29
|
+
# returns an array of databases present on the database-server #
|
30
|
+
|
31
|
+
def self.databases
|
32
|
+
get_data 'databases'
|
33
|
+
end
|
34
|
+
|
35
|
+
# ------------------------------ create database ------------------------------------------------- #
|
36
|
+
# creates a database if not present #
|
37
|
+
def self.create_database name
|
38
|
+
return if databases.include?( name.to_s )
|
39
|
+
payload = { "command" => "create database #{name}" }
|
40
|
+
post_data "server", payload
|
41
|
+
rescue HTTPX::HTTPError => e
|
42
|
+
logger.fatal "Create database #{name} through \"POST create/#{name}\" failed"
|
43
|
+
logger.fatal e
|
44
|
+
raise
|
45
|
+
end
|
46
|
+
|
47
|
+
# ------------------------------ drop database ------------------------------------------------- #
|
48
|
+
# deletes the given database #
|
49
|
+
def self.drop_database name
|
50
|
+
return unless databases.include?( name.to_s )
|
51
|
+
payload = {"command" => "drop database #{name}" }
|
52
|
+
post_data "server", payload
|
53
|
+
rescue HTTPX::HTTPError => e
|
54
|
+
logger.fatal "Drop database #{name} through \"POST drop database/#{name}\" failed"
|
55
|
+
raise
|
56
|
+
end
|
57
|
+
# ------------------------------ create document ------------------------------------------------- #
|
58
|
+
# adds a document to the specified database table
|
59
|
+
#
|
60
|
+
# specify database-fields as hash-type parameters
|
61
|
+
#
|
62
|
+
# i.e Arcade::Api.create_document 'devel', 'documents', name: 'herta meyer', age: 56, sex: 'f'
|
63
|
+
#
|
64
|
+
# returns the rid of the inserted dataset
|
65
|
+
#
|
66
|
+
def self.create_document database, type, **attributes
|
67
|
+
payload = { "@type" => type }.merge( attributes )
|
68
|
+
logger.debug "C: #{payload}"
|
69
|
+
options = if session.nil?
|
70
|
+
payload
|
71
|
+
else
|
72
|
+
payload.merge headers: { "arcadedb-session-id" => session }
|
73
|
+
end
|
74
|
+
post_data "document/#{database}", options
|
75
|
+
end
|
76
|
+
|
77
|
+
# ------------------------------ execute ------------------------------------------------- #
|
78
|
+
# executes a sql-query in the specified database
|
79
|
+
#
|
80
|
+
# the query is provided as block
|
81
|
+
#
|
82
|
+
# returns an Array of results (if propriate)
|
83
|
+
# i.e
|
84
|
+
# Arcade::Api.execute( "devel" ) { 'select from test ' }
|
85
|
+
# =y [{"@rid"=>"#57:0", "@type"=>"test", "name"=>"Hugo"}, {"@rid"=>"#60:0", "@type"=>"test", "name"=>"Hubert"}]
|
86
|
+
#
|
87
|
+
def self.execute database, query=nil, session_id= nil
|
88
|
+
pl = query.nil? ? provide_payload(yield) : provide_payload(query)
|
89
|
+
if session_id.nil? && session.nil?
|
90
|
+
post_data "command/#{database}" , pl
|
91
|
+
else
|
92
|
+
post_transaction "command/#{database}" , pl, session_id || session
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# ------------------------------ query ------------------------------------------------- #
|
97
|
+
# same for idempotent queries
|
98
|
+
def self.query database, query
|
99
|
+
post_data "query/#{database}" , provide_payload(query)
|
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 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
|
+
puts "R: #{r.inspect}"
|
140
|
+
if r.nil?
|
141
|
+
false
|
142
|
+
else
|
143
|
+
r[:operation] == 'create property'
|
144
|
+
end
|
145
|
+
end.uniq
|
146
|
+
if success == [true]
|
147
|
+
commit database
|
148
|
+
true
|
149
|
+
else
|
150
|
+
rollback database
|
151
|
+
end
|
152
|
+
|
153
|
+
|
154
|
+
end
|
155
|
+
|
156
|
+
# ------------------------------ index ------------------------------------------------- #
|
157
|
+
def self.index database, type, name , *properties
|
158
|
+
properties = properties.map( &:to_s )
|
159
|
+
unique_requested = "unique" if properties.delete("unique")
|
160
|
+
unique_requested = "notunique" if properties.delete("notunique" )
|
161
|
+
automatic = true if
|
162
|
+
properties << name if properties.empty?
|
163
|
+
# puts " create index #{type.to_s}[#{name.to_s}] on #{type} ( #{properties.join(',')} ) #{unique_requested}"
|
164
|
+
# VV 22.10: providing an index-name raises an Error ( Encountered " "(" "( "" at line 1, column 44. Was expecting one of: <EOF> <SCHEMA> ... <NULL_STRATEGY> ... ";" ... "," ... )) )
|
165
|
+
# named indices droped for now
|
166
|
+
success = execute(database) {" create index IF NOT EXISTS on #{type} (#{properties.join(', ')}) #{unique_requested}" } &.first
|
167
|
+
# puts "success: #{success}"
|
168
|
+
success[:operation] == 'create index'
|
169
|
+
|
170
|
+
end
|
171
|
+
|
172
|
+
|
173
|
+
private
|
174
|
+
|
175
|
+
def self.logger
|
176
|
+
Database.logger
|
177
|
+
end
|
178
|
+
|
179
|
+
def self.session
|
180
|
+
@session_id
|
181
|
+
end
|
182
|
+
|
183
|
+
def self. provide_payload( the_yield, action: :post )
|
184
|
+
unless the_yield.is_a? Hash
|
185
|
+
logger.info "Q: #{the_yield}"
|
186
|
+
the_yield = { :query => the_yield }
|
187
|
+
end
|
188
|
+
{ language: 'sql' }.merge(
|
189
|
+
the_yield.map do | key, value |
|
190
|
+
case key
|
191
|
+
when :query
|
192
|
+
action == :post ? [ :command, value ] : [ :query, value ]
|
193
|
+
when :limit
|
194
|
+
[ :limit , value ]
|
195
|
+
when :params
|
196
|
+
if value.is_a? Hash
|
197
|
+
[ :params, value ]
|
198
|
+
end
|
199
|
+
# serializer (optional) specify the serializer used for the result:
|
200
|
+
# graph: returns as a graph separating vertices from edges
|
201
|
+
# record: returns everything as records
|
202
|
+
# by default it’s like record but with additional metadata for vertex records,
|
203
|
+
# such as the number of outgoing edges in @out property and total incoming edges
|
204
|
+
# in @in property. This serialzier is used by Studio
|
205
|
+
when :serializer
|
206
|
+
if [:graph, :record].include? value.to_sym
|
207
|
+
[ :serializer, value.to_sym ]
|
208
|
+
end
|
209
|
+
when :language
|
210
|
+
if [:sql, :cypher, :gremlin, :neo4j ].include? value.to_sym
|
211
|
+
[ :language, value.to_sym ]
|
212
|
+
end
|
213
|
+
end # case
|
214
|
+
end .to_h ) # map
|
215
|
+
end
|
216
|
+
|
217
|
+
|
218
|
+
|
219
|
+
# returns the json-response ## retiered
|
220
|
+
def self.analyse_result r, command
|
221
|
+
if r.success?
|
222
|
+
return nil if r.status == 204 # no content
|
223
|
+
result = JSON.parse( r.response_body, symbolize_names: true )[:result]
|
224
|
+
if result == [{}]
|
225
|
+
[]
|
226
|
+
else
|
227
|
+
result
|
228
|
+
end
|
229
|
+
elsif r.timed_out?
|
230
|
+
raise Error "Timeout Error", caller
|
231
|
+
[]
|
232
|
+
elsif r.response_code > 0
|
233
|
+
logger.error "Execution Failure – Code: #{ r.response_code } – #{r.status_message} "
|
234
|
+
error_message = JSON.parse( r.response_body, symbolize_names: true )
|
235
|
+
logger.error "ErrorMessage: #{ error_message[:detail]} "
|
236
|
+
if error_message[:detail] =~ /Duplicated key/
|
237
|
+
raise IndexError, error_message[:detail]
|
238
|
+
else
|
239
|
+
# available fields: :detail, :exception, error
|
240
|
+
puts error_message[:detail]
|
241
|
+
#raise error_message[:detail], caller
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
def self.auth
|
246
|
+
@a ||= { httpauth: :basic,
|
247
|
+
username: Config.admin[:user],
|
248
|
+
password: Config.admin[:pass] }
|
249
|
+
end
|
250
|
+
|
251
|
+
# not tested
|
252
|
+
def self.delete_data command
|
253
|
+
result = HTTPX.delete Config.base_uri + command, auth
|
254
|
+
analyse_result(result, command)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module Arcade
|
2
|
+
module Api
|
3
|
+
module Primitives
|
4
|
+
|
5
|
+
# This module handles the interaction with the database through HTTPX
|
6
|
+
#
|
7
|
+
# ------------------------------ http ------------------------------------------------------------ #
|
8
|
+
# persistent http handle to the database
|
9
|
+
|
10
|
+
def http
|
11
|
+
break_on = -> (response) { response.status == 500 }
|
12
|
+
@http ||= HTTPX.plugin(:basic_auth).basic_auth(auth[:username], auth[:password])
|
13
|
+
.plugin(:persistent)
|
14
|
+
.plugin(:circuit_breaker)
|
15
|
+
# .plugin(:circuit_breaker, circuit_breaker_break_on: break_on)
|
16
|
+
end
|
17
|
+
|
18
|
+
# ------------------------------ get data -------------------------------------------------------- #
|
19
|
+
def get_data command
|
20
|
+
response = http.get( Config.base_uri + command )
|
21
|
+
response.raise_for_status
|
22
|
+
|
23
|
+
JSON.parse( response.body, symbolize_names: true )[:result]
|
24
|
+
# alternative to `raise for status `
|
25
|
+
# case response = http.basic_auth(auth[:username], auth[:password]).get( Config.base_uri + command )
|
26
|
+
# in { status: 200..203, body: }
|
27
|
+
# puts "success: #{JSON.parse(body, symbolize_names: true)[:result]}"
|
28
|
+
# in { status: 400..499, body: }
|
29
|
+
# puts "client error: #{body.json}"
|
30
|
+
# in { status: 500.., body: }
|
31
|
+
# puts "server error: #{body.to_s}"
|
32
|
+
# in { error: error }
|
33
|
+
# puts "error: #{error.message}"
|
34
|
+
# else
|
35
|
+
# raise "unexpected: #{response}"
|
36
|
+
# end
|
37
|
+
# puts "result : #{response}"
|
38
|
+
# puts "code: #{response.status}"
|
39
|
+
# analyse_result(response, command)
|
40
|
+
end
|
41
|
+
|
42
|
+
# ------------------------------ post data -------------------------------------------------------- #
|
43
|
+
def post_data command, payload
|
44
|
+
# http = HTTPX.plugin(:basic_auth)
|
45
|
+
# .basic_auth(auth[:username], auth[:password])
|
46
|
+
response = http.post( Config.base_uri + command, json: payload )
|
47
|
+
response.raise_for_status
|
48
|
+
JSON.parse( response.body, symbolize_names: true )[:result]
|
49
|
+
end
|
50
|
+
|
51
|
+
# ------------------------------ transaction ------------------------------------------------- #
|
52
|
+
#
|
53
|
+
def begin_transaction database
|
54
|
+
result = http.post Config.base_uri + "begin/#{database}"
|
55
|
+
@session_id = result.headers["arcadedb-session-id"]
|
56
|
+
# returns the session-id
|
57
|
+
end
|
58
|
+
|
59
|
+
# ------------------------------ post transaction ------------------------------------------------- #
|
60
|
+
def post_transaction command, params, session_id= @session_id
|
61
|
+
# http = HTTPX.plugin(:basic_auth)
|
62
|
+
# .basic_auth(auth[:username], auth[:password])
|
63
|
+
# .with( headers: { "arcadedb-session-id"=>session }, debug_level: 1)
|
64
|
+
http_a = http.with( headers: { "arcadedb-session-id" => session_id } , debug_level: 1)
|
65
|
+
response = http_a.post( Config.base_uri + command, json: params )
|
66
|
+
response.raise_for_status
|
67
|
+
JSON.parse( response.body, symbolize_names: true )[:result]
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
# ------------------------------ commit ------------------------------------------------- #
|
72
|
+
def commit database, session_id = @session_id
|
73
|
+
http_a = http.with( headers: { "arcadedb-session-id" => session_id } , debug_level: 1)
|
74
|
+
response = http_a.post( Config.base_uri + "commit/#{database}" )
|
75
|
+
response.raise_for_status
|
76
|
+
@session_id = nil
|
77
|
+
response.status # returns 204 --> success
|
78
|
+
# 403 --> incalid credentials
|
79
|
+
# 500 --> Transaction not begun
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
# ------------------------------ rollback ------------------------------------------------- #
|
84
|
+
def rollback database, session_id = @session_id, publish_error=true
|
85
|
+
# http = HTTPX.plugin(:basic_auth)
|
86
|
+
# .basic_auth(auth[:username], auth[:password])
|
87
|
+
# .with( headers: { "arcadedb-session-id"=>session_id }, debug_level: 1)
|
88
|
+
http_a = http.with( headers: { "arcadedb-session-id" => session_id } , debug_level: 1)
|
89
|
+
response = http_a.post( Config.base_uri + "rollback/#{database}" )
|
90
|
+
response.raise_for_status
|
91
|
+
@session_id = nil
|
92
|
+
logger.error "A Transaction has been rolled back" # if publish_error
|
93
|
+
response.status
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|