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
data/bin/+
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
## loads the active-orient environment
|
3
|
+
## and starts an interactive shell
|
4
|
+
##
|
5
|
+
## Parameter:
|
6
|
+
## production (p)
|
7
|
+
## development (d) [default]
|
8
|
+
## test (t)
|
9
|
+
require 'bundler/setup'
|
10
|
+
require 'yaml'
|
11
|
+
require 'logger'
|
12
|
+
require 'pastel'
|
13
|
+
require 'terminal-table'
|
14
|
+
require 'arcade'
|
15
|
+
#begin
|
16
|
+
|
17
|
+
module CoreExtensions
|
18
|
+
|
19
|
+
module Hash
|
20
|
+
module TablePresenter
|
21
|
+
def table_header
|
22
|
+
keys
|
23
|
+
end
|
24
|
+
def table_row
|
25
|
+
values
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module Array
|
31
|
+
module TablePresenter
|
32
|
+
def as_table(&b)
|
33
|
+
the_table_header = first.table_header(&b)
|
34
|
+
the_table_rows = map &:table_row
|
35
|
+
Terminal::Table.new headings: the_table_header, rows: the_table_rows , style: { border: :unicode }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
Array.include CoreExtensions::Array::TablePresenter
|
42
|
+
Hash.include CoreExtensions::Hash::TablePresenter
|
43
|
+
|
44
|
+
|
45
|
+
read_yml = -> (key) do
|
46
|
+
YAML::load_file( File.expand_path('../../config.yml',__FILE__) )[key]
|
47
|
+
end
|
48
|
+
def list_types db= Arcade::Api.databases.first
|
49
|
+
list= Arcade::Api.query(db){ "select from schema:types" }
|
50
|
+
puts "list: #{list}"
|
51
|
+
output = Terminal::Table.new do |t|
|
52
|
+
t.title = "database: #{db}"
|
53
|
+
t.headings = ["Name" , "Parents", "Type", 'Properties','Unique', 'Automatic']
|
54
|
+
t.style = { :border => :markdown }
|
55
|
+
list.each do |row|
|
56
|
+
t.add_row [ row['name'],row['parent_types'],row['type'] ]
|
57
|
+
if !row['indexes'].empty?
|
58
|
+
t.add_separator
|
59
|
+
row['indexes'].each do |i|
|
60
|
+
t.add_row [i['name'], i['typeName'], i['type'],i['properties'], i['unique'],i['automatic'] ]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
if !row['properties'].empty?
|
64
|
+
t.add_separator
|
65
|
+
row['properties'].each do |p|
|
66
|
+
t.add_row[p['name'],p['id'],p['type']]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
|
75
|
+
|
76
|
+
|
77
|
+
include Arcade
|
78
|
+
|
79
|
+
puts "-"*80
|
80
|
+
puts "--- Namespace Arcade is included "
|
81
|
+
puts ""
|
82
|
+
puts " Available ArcadeDB-Statements"
|
83
|
+
puts " ----------------------------"
|
84
|
+
puts " Api.databases # returns an Array of known databases "
|
85
|
+
puts " Api.create_database <a string> # returns true if succesfull "
|
86
|
+
puts " Api.drop_database <a string> "
|
87
|
+
puts ""
|
88
|
+
puts " Create and fetch documents and vertices"
|
89
|
+
puts " ----------------------------"
|
90
|
+
puts " Api.create_document <database>, <type>, attribute: value , ... "
|
91
|
+
puts " Api.get_record <database>, <rid> # returns a hash "
|
92
|
+
puts ""
|
93
|
+
puts " submit queries"
|
94
|
+
puts " ----------------------------"
|
95
|
+
puts " Api.execute( <database> ) { <query> } #submit query as block"
|
96
|
+
puts " Api.query( <database> ) { <query> } "
|
97
|
+
puts ""
|
98
|
+
puts "-"*80
|
99
|
+
puts ""
|
100
|
+
#C = Arcade::Database.new
|
101
|
+
#D = MiniSql::Connection.get( C.connection, {} )
|
102
|
+
#require 'pry'
|
103
|
+
require 'irb'
|
104
|
+
ARGV.clear
|
105
|
+
IRB.start(__FILE__)
|
106
|
+
#Pry.start(__FILE__)
|
data/bin/console
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
## loads the active-orient environment
|
3
|
+
## and starts an interactive shell
|
4
|
+
##
|
5
|
+
## Parameter:
|
6
|
+
## production (p)
|
7
|
+
## development (d) [default]
|
8
|
+
## test (t)
|
9
|
+
require 'bundler/setup'
|
10
|
+
require 'terminal-table'
|
11
|
+
require 'zeitwerk'
|
12
|
+
require 'pastel'
|
13
|
+
require 'arcade'
|
14
|
+
require 'pry'
|
15
|
+
#begin
|
16
|
+
|
17
|
+
|
18
|
+
def browse db: Arcade::Api.databases.first , extended: false
|
19
|
+
|
20
|
+
headings = -> do
|
21
|
+
ar= ["","Name" , "Parents", "Type","Properties", 'Unique', 'Automatic']
|
22
|
+
extended ? ar : ar - ['Properties']
|
23
|
+
end
|
24
|
+
rows = ->(r) do
|
25
|
+
ar=["Type", r[:name],r[:parent_types],r[:type]," " , " "]
|
26
|
+
!extended ? ar : ar + [""]
|
27
|
+
end
|
28
|
+
indizes = ->(i) do
|
29
|
+
if extended
|
30
|
+
["Index",i[:name], "on #{i[:typeName]}", i[:type],i[:properties], i[:unique],i[:automatic] ]
|
31
|
+
else
|
32
|
+
["Index",i[:name], "", i[:type], i[:unique],i[:automatic] ]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
list= Arcade::Api.query db, "select from schema:types"
|
37
|
+
if list.is_a? Array
|
38
|
+
Terminal::Table.new do |t|
|
39
|
+
add_additional_separator =false
|
40
|
+
t.title = "Database: #{db}"
|
41
|
+
t.headings = headings.call
|
42
|
+
# t.style = { :border => :markdown }
|
43
|
+
t.style = { border: :unicode_thick_edge }
|
44
|
+
list.each.with_index do |row,j|
|
45
|
+
t.add_separator if (add_additional_separator || !row[:indexes].empty? || !row[:properties].empty?) && j >0
|
46
|
+
add_additional_separator =false
|
47
|
+
t.add_row rows[row]
|
48
|
+
if !row[:indexes].empty?
|
49
|
+
add_additional_separator = true
|
50
|
+
row[:indexes].each do |i|
|
51
|
+
t.add_row indizes[i]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
if !row[:properties].empty?
|
55
|
+
add_additional_separator = true
|
56
|
+
row[:properties].each do |p|
|
57
|
+
ar= ["Property",p[:name],"",p[:type]," "," "]
|
58
|
+
t.add_row !extended ? ar : ar + [""]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
else
|
64
|
+
list
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
include Arcade
|
74
|
+
|
75
|
+
def help
|
76
|
+
p = Pastel.new
|
77
|
+
c = p.bright_white.bold.on_yellow.detach
|
78
|
+
g = p.green.on_black.detach
|
79
|
+
puts "-"*80
|
80
|
+
puts " Namespace is: Arcade ! "
|
81
|
+
puts ""
|
82
|
+
puts c[" Essential ArcadeDB-Statements "]
|
83
|
+
puts " ----------------------------"
|
84
|
+
puts g[" Api.databases "]+" # returns an Array of known databases "
|
85
|
+
puts g[" Api.create_database <a string> "] + " # returns true if successfull "
|
86
|
+
puts g[" Api.drop_database <a string> "]
|
87
|
+
puts ""
|
88
|
+
puts " Create and fetch documents and vertices"
|
89
|
+
puts " ----------------------------"
|
90
|
+
puts g[" Api.create_document <database>, <type>, attribute: value , ... "]
|
91
|
+
puts g[" Api.get_record <database>, <rid> "] +"# returns a hash "
|
92
|
+
puts ""
|
93
|
+
puts " submit queries"
|
94
|
+
puts " ----------------------------"
|
95
|
+
puts g[" Api.execute( <database> ) { <query> } "]+" #submit query as block"
|
96
|
+
puts g[" Api.query <database> , <query> "]
|
97
|
+
puts ""
|
98
|
+
puts " -----------------------------"
|
99
|
+
puts " Display the database-structure"
|
100
|
+
puts g[" puts browse db: \"name\", extended: \"false|true\" "]+ "browses the first database found, "
|
101
|
+
puts " if called without parameters"
|
102
|
+
puts " Display this helpscreen: "+ g["help"]
|
103
|
+
puts "-"*80
|
104
|
+
puts ""
|
105
|
+
end
|
106
|
+
|
107
|
+
help
|
108
|
+
e= ARGV.empty? ? :development : ARGV.last.downcase.to_sym
|
109
|
+
## load test model files
|
110
|
+
#require "#{__dir__}/../spec/model_helper"
|
111
|
+
|
112
|
+
loader = Zeitwerk::Loader.new
|
113
|
+
loader.push_dir ("#{__dir__}/../spec/model")
|
114
|
+
loader.setup
|
115
|
+
puts "DB = Arcade::Database-instance"
|
116
|
+
DB = Arcade::Init.connect e
|
117
|
+
#require 'pry'
|
118
|
+
require 'irb'
|
119
|
+
ARGV.clear
|
120
|
+
#begin
|
121
|
+
#IRB.start(__FILE__)
|
122
|
+
#rescue ArgumentError => e
|
123
|
+
# puts e
|
124
|
+
## retry
|
125
|
+
#end
|
126
|
+
Pry.start(__FILE__)
|
data/examples/books.rb
ADDED
@@ -0,0 +1,139 @@
|
|
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 create a simple graph:
|
9
|
+
|
10
|
+
Book --> HasContent --> KeyWord
|
11
|
+
|
12
|
+
and to query it dynamically using Arcade::Query
|
13
|
+
|
14
|
+
The model-files are located in `/spec/model/ex`
|
15
|
+
|
16
|
+
There are 3 default search items provided. They are used if no parameter is given.
|
17
|
+
However, any parameter given is transmitted as serach-criteria, ie.
|
18
|
+
ruby books.rb Juli August
|
19
|
+
defines two search criteria.
|
20
|
+
|
21
|
+
=end
|
22
|
+
require 'bundler/setup'
|
23
|
+
require 'yaml'
|
24
|
+
require 'zeitwerk'
|
25
|
+
require 'arcade'
|
26
|
+
|
27
|
+
include Arcade
|
28
|
+
## require modelfiles
|
29
|
+
loader = Zeitwerk::Loader.new
|
30
|
+
loader.push_dir ("#{__dir__}/../spec/model")
|
31
|
+
loader.setup
|
32
|
+
|
33
|
+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
|
34
|
+
################# read samples ##############
|
35
|
+
def read_samples
|
36
|
+
print "\n === READ SAMPLES === \n"
|
37
|
+
## Lambda fill databasea
|
38
|
+
# Anaylse a sentence
|
39
|
+
# Split into words and upsert them to Word-Class.
|
40
|
+
# The Block is only performed, if a new Word is inserted.
|
41
|
+
fill_database = ->(sentence, this_book ) do
|
42
|
+
|
43
|
+
sentence.split(' ').map do | word |
|
44
|
+
this_book.assign vertex: Ex::Keyword.create( item: word ), via: Ex::HasContent if Ex::Keyword.where( item: word ).empty?
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
## Lambda end
|
49
|
+
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 heute mit. Bei Werten im positiven Bereich signalisiert das Barometer ein Wachstum Ökonomen hatten eigentlich mit einem Anstieg auf 5,0 Punkte gerechnet'
|
50
|
+
this_book = Ex::Book.create title: 'first'
|
51
|
+
fill_database[ words, this_book ]
|
52
|
+
|
53
|
+
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'
|
54
|
+
this_book = Ex::Book.create title: 'second'
|
55
|
+
fill_database[ words2, this_book ]
|
56
|
+
puts "#{Ex::Keyword.count} keywords inserted into Database"
|
57
|
+
end
|
58
|
+
|
59
|
+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
|
60
|
+
################# display books ##############
|
61
|
+
def display_books_with *desired_words
|
62
|
+
## Each serach criteria becomes a subquery
|
63
|
+
## This is integrated into the main_query using 'let'.
|
64
|
+
## Subquery: let ${a-y} = select expand(in('has_content')) from keyword where item = {search_word}
|
65
|
+
## combine with intercect: $z = Intercect( $a ... $y )
|
66
|
+
## Main Query is just
|
67
|
+
## select expand( $z ) followed by all let-statements and finalized by "intercect"
|
68
|
+
#
|
69
|
+
|
70
|
+
# lambda to create subqueries
|
71
|
+
word_query = -> (arg) do
|
72
|
+
q= Ex::Keyword.query where: arg
|
73
|
+
q.nodes :in, via: Ex::HasContent
|
74
|
+
end
|
75
|
+
|
76
|
+
main_query = Arcade::Query.new
|
77
|
+
|
78
|
+
## We just create "select expand($z)"
|
79
|
+
## without 'expand' the result is "$z" -> "in(ex_has_content)"-> [ book-record ]
|
80
|
+
## with 'expand' its "in(ex_has_content)"-> [ book-record ]
|
81
|
+
main_query.expand( '$z' )
|
82
|
+
|
83
|
+
|
84
|
+
print("\n === display_books_with » #{ desired_words.join "," } « === \n")
|
85
|
+
|
86
|
+
intersects = Array.new
|
87
|
+
## Now, add subqueries to the main-query
|
88
|
+
desired_words.each_with_index do | word, i |
|
89
|
+
symbol = ( i+97 ).chr # convert 1 -> 'a', 2 -> 'b' ...
|
90
|
+
main_query.let symbol => word_query[ item: word ]
|
91
|
+
intersects << "$#{symbol}"
|
92
|
+
end
|
93
|
+
## Finally add the intersects statement
|
94
|
+
## Use UnionAll for an superposition of positive seraches
|
95
|
+
## Use Intercept for a positive search only if all parameters apply to one book.
|
96
|
+
#
|
97
|
+
main_query.let "$z = unionall(#{intersects.join(', ')}) "
|
98
|
+
#main_query.let "$z = Intersect(#{intersects.join(', ')}) "
|
99
|
+
puts "generated Query:"
|
100
|
+
puts main_query.to_s
|
101
|
+
puts "\n\n\n"
|
102
|
+
result = main_query.query.select_result
|
103
|
+
puts '-' * 23
|
104
|
+
print "found books: "
|
105
|
+
puts result.map( &:title ).uniq.join("; ")
|
106
|
+
if result.empty?
|
107
|
+
puts " -- None -- "
|
108
|
+
puts " try » ruby books.rb Japan Flaute « for a positive search in one of the two sentences"
|
109
|
+
else
|
110
|
+
puts '-_' * 23
|
111
|
+
puts "that's it folks"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
if $0 == __FILE__
|
116
|
+
|
117
|
+
search_items = ARGV.empty? ? ['China', 'aus', 'Flaute'] : ARGV
|
118
|
+
|
119
|
+
## clear test database
|
120
|
+
databases = Arcade::Api.databases
|
121
|
+
if databases.include?(Arcade::Config.database[:test])
|
122
|
+
Arcade::Api.drop_database Arcade::Config.database[:test]
|
123
|
+
end
|
124
|
+
Arcade::Api.create_database Arcade::Config.database[:test]
|
125
|
+
|
126
|
+
Arcade::Init.connect 'test'
|
127
|
+
|
128
|
+
print "\n === REBUILD finished=== \n"
|
129
|
+
## check wether the database tables exist. Then delete Database-Class and preallocated ruby-Object
|
130
|
+
print " creating Book and Keyword as Vertex; HasContent as Edge \n"
|
131
|
+
Ex::Book.create_type
|
132
|
+
Ex::Keyword.create_type
|
133
|
+
Ex::HasContent.create_type
|
134
|
+
print "\n === PROPERTY === \n"
|
135
|
+
|
136
|
+
read_samples
|
137
|
+
display_books_with *search_items
|
138
|
+
|
139
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
##
|
2
|
+
## This example realises an unidirectional Graph using
|
3
|
+
## a document database primitve and a self referencing 1:1 relation.
|
4
|
+
#
|
5
|
+
## Modelfiles are located in spec/model.
|
6
|
+
## The exampmle runs in the test environment.
|
7
|
+
##
|
8
|
+
require 'bundler/setup'
|
9
|
+
require 'zeitwerk'
|
10
|
+
require 'arcade'
|
11
|
+
|
12
|
+
include Arcade # Arcade Namespace is optional
|
13
|
+
## require modelfiles
|
14
|
+
loader = Zeitwerk::Loader.new
|
15
|
+
loader.push_dir ("#{__dir__}/../spec/model")
|
16
|
+
loader.setup
|
17
|
+
|
18
|
+
## clear test database
|
19
|
+
|
20
|
+
databases = Api.databases
|
21
|
+
if databases.include?(Config.database[:test])
|
22
|
+
Api.drop_database Config.database[:test]
|
23
|
+
end
|
24
|
+
Api.create_database Config.database[:test]
|
25
|
+
|
26
|
+
## Universal Database handle
|
27
|
+
DB = Init.connect 'test'
|
28
|
+
|
29
|
+
## ------------------------------------------------------ End Setup ------------------------------------- ##
|
30
|
+
##
|
31
|
+
## We are realising a self referencing relation
|
32
|
+
## name --> child
|
33
|
+
#
|
34
|
+
# database schema
|
35
|
+
# CREATE DOCUMENT TYPE ex_names
|
36
|
+
# CREATE PROPERTY ex_names.child LINK
|
37
|
+
# CREATE PROPERTY ex_names.name STRING
|
38
|
+
# CREATE INDEX `Example[names]` ON ex_names ( name ) UNIQUE
|
39
|
+
|
40
|
+
|
41
|
+
# Namespace for Data-Objects is `Ex`
|
42
|
+
Ex::Names.create_type # initialize the database
|
43
|
+
# spec/models/ex/names.rb
|
44
|
+
# Put some names into the database
|
45
|
+
puts "-------------------- insert data ---------------------------------"
|
46
|
+
table = %w( Guthorn Fulkerson Sniezek Tomasulo Portwine Keala Revelli Jacks Gorby Alcaoa ).map do | name |
|
47
|
+
Ex::Names.insert name: name, age: rand(99)
|
48
|
+
end
|
49
|
+
# Connect randomly
|
50
|
+
children = (0..6).map{ | i | table[-i].rid }
|
51
|
+
table.each{| n | n.update child: table[ rand(10)].rid }
|
52
|
+
|
53
|
+
puts "-------------------- raw data ---------------------------------"
|
54
|
+
puts Ex::Names.all autoload: false # display links as rid (do not load the document)
|
55
|
+
|
56
|
+
puts "--------------- Parent and Children ---------------------------"
|
57
|
+
|
58
|
+
## Create a Query-Object and execute it via `query`
|
59
|
+
children_query = Ex::Names.query projection: ['name', 'child.name']
|
60
|
+
puts children_query.query
|
61
|
+
|
62
|
+
puts "--------------- unidirectional relation -----------------------"
|
63
|
+
|
64
|
+
## Use the `Model.all` approach instead
|
65
|
+
puts Ex::Names.all projection: ['name as parent',
|
66
|
+
'age as parent_age',
|
67
|
+
'child.age',
|
68
|
+
'child.child.age as second_generation_age'],
|
69
|
+
order: 'parent_age'
|
70
|
+
|
71
|
+
|
72
|
+
puts "----------------- query relation - teenager -------------------"
|
73
|
+
|
74
|
+
teenager_query = Ex::Names.query projection: ['name as parent',
|
75
|
+
'child:{name, age} as teenager'],
|
76
|
+
where: 'child.age between 10 and 25'
|
77
|
+
|
78
|
+
puts teenager_query.query
|
79
|
+
|
80
|
+
__END__
|
81
|
+
|
82
|
+
Expected result
|
83
|
+
|
84
|
+
24.04.(11:12:58) INFO->Q: create document type ex_names
|
85
|
+
24.04.(11:12:58) INFO->Q: CREATE PROPERTY ex_names.child LINK
|
86
|
+
24.04.(11:12:58) INFO->Q: CREATE PROPERTY ex_names.name STRING
|
87
|
+
24.04.(11:12:58) INFO->Q: CREATE INDEX `Example[names]` ON ex_names ( name ) UNIQUE
|
88
|
+
-------------------- insert data ---------------------------------
|
89
|
+
24.04.(11:12:58) INFO->Q: INSERT INTO ex_names CONTENT {"name":"Guthorn","age":81}
|
90
|
+
24.04.(11:12:58) INFO->Q: INSERT INTO ex_names CONTENT {"name":"Fulkerson","age":16}
|
91
|
+
24.04.(11:12:58) INFO->Q: INSERT INTO ex_names CONTENT {"name":"Sniezek","age":50}
|
92
|
+
24.04.(11:12:58) INFO->Q: INSERT INTO ex_names CONTENT {"name":"Tomasulo","age":20}
|
93
|
+
24.04.(11:12:58) INFO->Q: INSERT INTO ex_names CONTENT {"name":"Portwine","age":7}
|
94
|
+
24.04.(11:12:58) INFO->Q: INSERT INTO ex_names CONTENT {"name":"Keala","age":57}
|
95
|
+
24.04.(11:12:58) INFO->Q: INSERT INTO ex_names CONTENT {"name":"Revelli","age":31}
|
96
|
+
24.04.(11:12:58) INFO->Q: INSERT INTO ex_names CONTENT {"name":"Jacks","age":23}
|
97
|
+
24.04.(11:12:58) INFO->Q: INSERT INTO ex_names CONTENT {"name":"Gorby","age":83}
|
98
|
+
24.04.(11:12:58) INFO->Q: INSERT INTO ex_names CONTENT {"name":"Alcaoa","age":58}
|
99
|
+
24.04.(11:12:58) INFO->Q: update #1:0 set child = '#2:1' return after $current
|
100
|
+
24.04.(11:12:58) INFO->Q: update #2:0 set child = '#3:0' return after $current
|
101
|
+
24.04.(11:12:58) INFO->Q: update #3:0 set child = '#1:0' return after $current
|
102
|
+
24.04.(11:12:58) INFO->Q: update #4:0 set child = '#1:1' return after $current
|
103
|
+
24.04.(11:12:58) INFO->Q: update #5:0 set child = '#2:1' return after $current
|
104
|
+
24.04.(11:12:58) INFO->Q: update #6:0 set child = '#2:1' return after $current
|
105
|
+
24.04.(11:12:58) INFO->Q: update #7:0 set child = '#8:0' return after $current
|
106
|
+
24.04.(11:12:58) INFO->Q: update #8:0 set child = '#6:0' return after $current
|
107
|
+
24.04.(11:12:58) INFO->Q: update #1:1 set child = '#4:0' return after $current
|
108
|
+
24.04.(11:12:58) INFO->Q: update #2:1 set child = '#1:1' return after $current
|
109
|
+
-------------------- raw data ---------------------------------
|
110
|
+
24.04.(11:12:58) INFO->Q: select from ex_names
|
111
|
+
<ex_names[#1:0]: age : 81, child : <ex_names[#2:1]: age : 58, child : < ex_names: #1:1 >, name : Alcaoa>, name : Guthorn>
|
112
|
+
<ex_names[#1:1]: age : 83, child : <ex_names[#4:0]: age : 20, child : < ex_names: #1:1 >, name : Tomasulo>, name : Gorby>
|
113
|
+
<ex_names[#2:0]: age : 16, child : <ex_names[#3:0]: age : 50, child : < ex_names: #1:0 >, name : Sniezek>, name : Fulkerson>
|
114
|
+
<ex_names[#2:1]: age : 58, child : <ex_names[#1:1]: age : 83, child : < ex_names: #4:0 >, name : Gorby>, name : Alcaoa>
|
115
|
+
<ex_names[#3:0]: age : 50, child : <ex_names[#1:0]: age : 81, child : < ex_names: #2:1 >, name : Guthorn>, name : Sniezek>
|
116
|
+
<ex_names[#4:0]: age : 20, child : <ex_names[#1:1]: age : 83, child : < ex_names: #4:0 >, name : Gorby>, name : Tomasulo>
|
117
|
+
<ex_names[#5:0]: age : 7, child : <ex_names[#2:1]: age : 58, child : < ex_names: #1:1 >, name : Alcaoa>, name : Portwine>
|
118
|
+
<ex_names[#6:0]: age : 57, child : <ex_names[#2:1]: age : 58, child : < ex_names: #1:1 >, name : Alcaoa>, name : Keala>
|
119
|
+
<ex_names[#7:0]: age : 31, child : <ex_names[#8:0]: age : 23, child : < ex_names: #6:0 >, name : Jacks>, name : Revelli>
|
120
|
+
<ex_names[#8:0]: age : 23, child : <ex_names[#6:0]: age : 57, child : < ex_names: #2:1 >, name : Keala>, name : Jacks>
|
121
|
+
--------------- Parent and Children ---------------------------
|
122
|
+
24.04.(11:12:58) INFO->Q: select name, child.name from ex_names
|
123
|
+
{:name=>"Guthorn", :"child.name"=>"Alcaoa"}
|
124
|
+
{:name=>"Gorby", :"child.name"=>"Tomasulo"}
|
125
|
+
{:name=>"Fulkerson", :"child.name"=>"Sniezek"}
|
126
|
+
{:name=>"Alcaoa", :"child.name"=>"Gorby"}
|
127
|
+
{:name=>"Sniezek", :"child.name"=>"Guthorn"}
|
128
|
+
{:name=>"Tomasulo", :"child.name"=>"Gorby"}
|
129
|
+
{:name=>"Portwine", :"child.name"=>"Alcaoa"}
|
130
|
+
{:name=>"Keala", :"child.name"=>"Alcaoa"}
|
131
|
+
{:name=>"Revelli", :"child.name"=>"Jacks"}
|
132
|
+
{:name=>"Jacks", :"child.name"=>"Keala"}
|
133
|
+
--------------- unidirectional relation -----------------------
|
134
|
+
24.04.(11:12:58) INFO->Q: select name as parent, age as parent_age, child.age, child.child.age as second_generation_age from ex_names order by parent_age
|
135
|
+
{:parent=>"Portwine", :parent_age=>7, :second_generation_age=>83, :"child.age"=>58}
|
136
|
+
{:parent=>"Fulkerson", :parent_age=>16, :second_generation_age=>81, :"child.age"=>50}
|
137
|
+
{:parent=>"Tomasulo", :parent_age=>20, :second_generation_age=>20, :"child.age"=>83}
|
138
|
+
{:parent=>"Jacks", :parent_age=>23, :second_generation_age=>58, :"child.age"=>57}
|
139
|
+
{:parent=>"Revelli", :parent_age=>31, :second_generation_age=>57, :"child.age"=>23}
|
140
|
+
{:parent=>"Sniezek", :parent_age=>50, :second_generation_age=>58, :"child.age"=>81}
|
141
|
+
{:parent=>"Keala", :parent_age=>57, :second_generation_age=>83, :"child.age"=>58}
|
142
|
+
{:parent=>"Alcaoa", :parent_age=>58, :second_generation_age=>20, :"child.age"=>83}
|
143
|
+
{:parent=>"Guthorn", :parent_age=>81, :second_generation_age=>83, :"child.age"=>58}
|
144
|
+
{:parent=>"Gorby", :parent_age=>83, :second_generation_age=>83, :"child.age"=>20}
|
145
|
+
----------------- query relation - teenager -------------------
|
146
|
+
24.04.(11:12:58) INFO->Q: select name as parent, child:{name, age} as teenager from ex_names where child.age between 10 and 25
|
147
|
+
{:parent=>"Gorby", :teenager=>{:name=>"Tomasulo", :age=>20}}
|
148
|
+
{:parent=>"Revelli", :teenager=>{:name=>"Jacks", :age=>23}}
|
149
|
+
|
@@ -0,0 +1,56 @@
|
|
1
|
+
##
|
2
|
+
## This example realises a 1:n relation using a `embedded`property
|
3
|
+
#
|
4
|
+
## Its implemented in modelfiles located in spec/model
|
5
|
+
# and runs in the test environment.
|
6
|
+
##
|
7
|
+
require 'bundler/setup'
|
8
|
+
require 'zeitwerk'
|
9
|
+
require 'arcade'
|
10
|
+
|
11
|
+
include Arcade
|
12
|
+
## require modelfiles
|
13
|
+
loader = Zeitwerk::Loader.new
|
14
|
+
loader.push_dir ("#{__dir__}/../spec/model")
|
15
|
+
loader.setup
|
16
|
+
|
17
|
+
## clear test database
|
18
|
+
|
19
|
+
databases = Arcade::Api.databases
|
20
|
+
if databases.include?(Arcade::Config.database[:test])
|
21
|
+
Arcade::Api.drop_database Arcade::Config.database[:test]
|
22
|
+
end
|
23
|
+
Arcade::Api.create_database Arcade::Config.database[:test]
|
24
|
+
|
25
|
+
## Universal Database handle
|
26
|
+
DB = Arcade::Init.connect 'test'
|
27
|
+
|
28
|
+
## ------------------------------------------------------ End Setup ------------------------------------- ##
|
29
|
+
##
|
30
|
+
## We are realising a self referencing relation
|
31
|
+
## name --> children
|
32
|
+
#
|
33
|
+
|
34
|
+
|
35
|
+
Ex::NewNames.create_type # initialize the database
|
36
|
+
# spec/models/ex/new_names.rb
|
37
|
+
# Put same names into the database
|
38
|
+
table = %w( Guthorn Fulkerson Sniezek Tomasulo Portwine Keala Revelli Jacks Gorby Alcaoa ).map do | name |
|
39
|
+
Ex::NewNames.insert name: name, age: rand(99)
|
40
|
+
end
|
41
|
+
# Connect randomly
|
42
|
+
children = (0..6).map{ | i | table[-i].rid }
|
43
|
+
puts children
|
44
|
+
i = 0
|
45
|
+
table.each.with_index do | n, i |
|
46
|
+
n.update children: [ children[i] &.rid, table[ rand(10)+5] &.rid].compact
|
47
|
+
end
|
48
|
+
|
49
|
+
puts "-------------------- raw data ---------------------------"
|
50
|
+
puts Ex::NewNames.all false
|
51
|
+
|
52
|
+
puts "--------------- Parent and Children ---------------------"
|
53
|
+
|
54
|
+
children_query = Ex::NewNames.query projection: ['name as parent', 'children[age]']
|
55
|
+
puts children_query.query
|
56
|
+
|