ruby-kuzu 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data/History.md +6 -0
- data/LICENSE.txt +20 -0
- data/README.md +95 -0
- data/ext/kuzu_ext/config.c +318 -0
- data/ext/kuzu_ext/connection.c +310 -0
- data/ext/kuzu_ext/database.c +196 -0
- data/ext/kuzu_ext/extconf.rb +20 -0
- data/ext/kuzu_ext/kuzu_ext.c +158 -0
- data/ext/kuzu_ext/kuzu_ext.h +130 -0
- data/ext/kuzu_ext/node.c +24 -0
- data/ext/kuzu_ext/prepared_statement.c +392 -0
- data/ext/kuzu_ext/query_summary.c +140 -0
- data/ext/kuzu_ext/recursive_rel.c +24 -0
- data/ext/kuzu_ext/rel.c +24 -0
- data/ext/kuzu_ext/result.c +514 -0
- data/ext/kuzu_ext/types.c +612 -0
- data/lib/kuzu/config.rb +70 -0
- data/lib/kuzu/connection.rb +51 -0
- data/lib/kuzu/database.rb +53 -0
- data/lib/kuzu/node.rb +46 -0
- data/lib/kuzu/prepared_statement.rb +42 -0
- data/lib/kuzu/query_summary.rb +28 -0
- data/lib/kuzu/recursive_rel.rb +37 -0
- data/lib/kuzu/rel.rb +51 -0
- data/lib/kuzu/result.rb +139 -0
- data/lib/kuzu.rb +79 -0
- data/spec/kuzu/config_spec.rb +98 -0
- data/spec/kuzu/database_spec.rb +51 -0
- data/spec/kuzu/prepared_statement_spec.rb +93 -0
- data/spec/kuzu/query_summary_spec.rb +30 -0
- data/spec/kuzu/result_spec.rb +190 -0
- data/spec/kuzu/types_spec.rb +254 -0
- data/spec/kuzu_spec.rb +55 -0
- data/spec/spec_helper.rb +101 -0
- data.tar.gz.sig +0 -0
- metadata +163 -0
- metadata.gz.sig +0 -0
@@ -0,0 +1,93 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require_relative '../spec_helper'
|
4
|
+
|
5
|
+
require 'kuzu/prepared_statement'
|
6
|
+
|
7
|
+
|
8
|
+
RSpec.describe( Kuzu::PreparedStatement ) do
|
9
|
+
|
10
|
+
let( :db ) { Kuzu.database }
|
11
|
+
let( :connection ) { db.connect }
|
12
|
+
|
13
|
+
let( :schema ) { load_test_data( 'demo-db/schema.cypher' ) }
|
14
|
+
let( :copy_statements ) { load_test_data( 'demo-db/copy.cypher' ) }
|
15
|
+
|
16
|
+
|
17
|
+
def setup_demo_db
|
18
|
+
connection.run( schema )
|
19
|
+
connection.run( copy_statements )
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
before( :each ) do
|
24
|
+
setup_demo_db()
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
#
|
29
|
+
# Specs
|
30
|
+
#
|
31
|
+
|
32
|
+
it "can be created via a connection and a query string" do
|
33
|
+
statement = described_class.new( connection, <<~END_OF_QUERY )
|
34
|
+
MATCH (u:User)
|
35
|
+
WHERE u.age > $min_age and u.age < $max_age
|
36
|
+
RETURN u.name
|
37
|
+
END_OF_QUERY
|
38
|
+
|
39
|
+
expect( statement ).to be_a( described_class )
|
40
|
+
expect( statement ).to be_success
|
41
|
+
expect( statement.connection ).to be( connection )
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
it "doesn't error if executed before its variables are bound" do
|
46
|
+
statement = described_class.new( connection, <<~END_OF_QUERY )
|
47
|
+
MATCH (u:User)
|
48
|
+
WHERE u.age > $min_age and u.age < $max_age
|
49
|
+
RETURN u.name
|
50
|
+
END_OF_QUERY
|
51
|
+
|
52
|
+
result = statement.execute
|
53
|
+
|
54
|
+
expect( result.num_tuples ).to eq( 0 )
|
55
|
+
|
56
|
+
result.finish
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
it "can be executed if all of its variables are bound" do
|
61
|
+
statement = described_class.new( connection, <<~END_OF_QUERY )
|
62
|
+
MATCH (u:User)
|
63
|
+
WHERE u.age >= $min_age and u.age <= $max_age
|
64
|
+
RETURN u.name
|
65
|
+
END_OF_QUERY
|
66
|
+
|
67
|
+
result = statement.execute( min_age: 40, max_age: 50 )
|
68
|
+
|
69
|
+
expect( result.num_tuples ).to eq( 2 )
|
70
|
+
|
71
|
+
result.finish
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
it "can be reused" do
|
76
|
+
statement = described_class.new( connection, <<~END_OF_QUERY )
|
77
|
+
MATCH (u:User)
|
78
|
+
WHERE u.age >= $min_age and u.age <= $max_age
|
79
|
+
RETURN u.name
|
80
|
+
END_OF_QUERY
|
81
|
+
|
82
|
+
result = statement.execute( min_age: 40, max_age: 50 )
|
83
|
+
expect( result ).to be_success
|
84
|
+
expect( result.num_tuples ).to eq( 2 )
|
85
|
+
result.finish
|
86
|
+
|
87
|
+
result = statement.execute( min_age: 5, max_age: 90 )
|
88
|
+
expect( result ).to be_success
|
89
|
+
expect( result.num_tuples ).to eq( 4 )
|
90
|
+
result.finish
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require_relative '../spec_helper'
|
4
|
+
|
5
|
+
require 'kuzu/query_summary'
|
6
|
+
|
7
|
+
|
8
|
+
RSpec.describe( Kuzu::QuerySummary ) do
|
9
|
+
|
10
|
+
CREATE_TABLE_STATEMENTS = [
|
11
|
+
'CREATE NODE TABLE User(name STRING, age INT64, PRIMARY KEY (name))',
|
12
|
+
'CREATE NODE TABLE City(name STRING, population INT64, PRIMARY KEY (name))',
|
13
|
+
]
|
14
|
+
|
15
|
+
|
16
|
+
let( :db ) { Kuzu.database }
|
17
|
+
let( :connection ) { db.connect }
|
18
|
+
|
19
|
+
|
20
|
+
it "can return a summary of its query's timing" do
|
21
|
+
connection.query( CREATE_TABLE_STATEMENTS.first ) do |result|
|
22
|
+
summary = result.query_summary
|
23
|
+
|
24
|
+
expect( summary ).to be_a( described_class )
|
25
|
+
expect( summary.compiling_time ).to be_a( Float ).and( be > 0.0 )
|
26
|
+
expect( summary.execution_time ).to be_a( Float ).and( be > 0.0 )
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require_relative '../spec_helper'
|
4
|
+
|
5
|
+
require 'kuzu/result'
|
6
|
+
|
7
|
+
|
8
|
+
RSpec.describe( Kuzu::Result ) do
|
9
|
+
|
10
|
+
let( :db ) { Kuzu.database }
|
11
|
+
let( :connection ) { db.connect }
|
12
|
+
|
13
|
+
let( :schema ) { load_test_data( 'demo-db/schema.cypher' ) }
|
14
|
+
let( :copy_statements ) { load_test_data( 'demo-db/copy.cypher' ) }
|
15
|
+
|
16
|
+
|
17
|
+
def setup_demo_db
|
18
|
+
connection.run( schema )
|
19
|
+
connection.run( copy_statements )
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
#
|
24
|
+
# Specs
|
25
|
+
#
|
26
|
+
|
27
|
+
describe "query constructor" do
|
28
|
+
|
29
|
+
it "can be created via a simple query" do
|
30
|
+
result = described_class.from_query( connection, schema.each_line.first )
|
31
|
+
|
32
|
+
expect( result ).to be_a( described_class )
|
33
|
+
expect( result ).to be_success
|
34
|
+
|
35
|
+
result.finish
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
it "automatically finishes the result when run with a block" do
|
40
|
+
block_result = nil
|
41
|
+
|
42
|
+
described_class.from_query( connection, schema.each_line.first ) do |result|
|
43
|
+
block_result = result
|
44
|
+
expect( result ).to be_a( described_class )
|
45
|
+
expect( result ).to be_success
|
46
|
+
end
|
47
|
+
|
48
|
+
expect( block_result ).to be_finished
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
it "can return a summary of its query's timing" do
|
53
|
+
result = described_class.from_query( connection, schema.each_line.first )
|
54
|
+
summary = result.query_summary
|
55
|
+
|
56
|
+
expect( summary ).to be_a( Kuzu::QuerySummary )
|
57
|
+
|
58
|
+
result.finish
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
it "can return the error message if there was a problem with the query" do
|
63
|
+
expect {
|
64
|
+
described_class.from_query( connection, "FOOBANGLE NERFRIDER" )
|
65
|
+
}.to raise_error( Kuzu::QueryError, /parser exception/i )
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
it "can iterate over result tuples" do
|
70
|
+
setup_demo_db()
|
71
|
+
|
72
|
+
result = described_class.from_query( connection, <<~END_OF_QUERY )
|
73
|
+
MATCH ( a:User )-[ f:Follows ]->( b:User )
|
74
|
+
RETURN a.name, b.name, f.since;
|
75
|
+
END_OF_QUERY
|
76
|
+
|
77
|
+
expect( result ).to be_a( described_class )
|
78
|
+
expect( result ).to be_success
|
79
|
+
expect( result.num_columns ).to eq( 3 )
|
80
|
+
expect( result.column_names ).to eq([ "a.name", "b.name", "f.since" ])
|
81
|
+
expect( result.each.to_a ).to eq([
|
82
|
+
{ "a.name" => "Adam", "b.name" => "Karissa", "f.since" => 2020 },
|
83
|
+
{ "a.name" => "Adam", "b.name" => "Zhang", "f.since" => 2020 },
|
84
|
+
{ "a.name" => "Karissa", "b.name" => "Zhang", "f.since" => 2021 },
|
85
|
+
{ "a.name" => "Zhang", "b.name" => "Noura", "f.since" => 2022 }
|
86
|
+
])
|
87
|
+
|
88
|
+
result.finish
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
it "can iterate over result sets" do
|
93
|
+
result = described_class.from_query( connection, <<~END_QUERY )
|
94
|
+
return 1;
|
95
|
+
return 2;
|
96
|
+
return 3;
|
97
|
+
END_QUERY
|
98
|
+
|
99
|
+
rval = result.each_set.flat_map do |subset|
|
100
|
+
subset.each.to_a
|
101
|
+
end
|
102
|
+
|
103
|
+
expect( rval ).to eq([
|
104
|
+
{ "1" => 1 },
|
105
|
+
{ "2" => 2 },
|
106
|
+
{ "3" => 3 },
|
107
|
+
])
|
108
|
+
|
109
|
+
result.finish
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
it "also finishes successive results if they've been fetched when the first one is finished" do
|
114
|
+
result = described_class.from_query( connection, <<~END_QUERY )
|
115
|
+
return 1;
|
116
|
+
return 2;
|
117
|
+
return 3;
|
118
|
+
END_QUERY
|
119
|
+
|
120
|
+
second_result = result.next_set
|
121
|
+
|
122
|
+
result.finish
|
123
|
+
|
124
|
+
expect( second_result ).to be_finished
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
it "errors if it's used after being finished" do
|
129
|
+
result = described_class.from_query( connection, "return 1;" )
|
130
|
+
result.finish
|
131
|
+
|
132
|
+
expect( result ).to be_finished
|
133
|
+
|
134
|
+
expect {
|
135
|
+
result.get_next_values
|
136
|
+
}.to raise_error( Kuzu::FinishedError )
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
describe "prepared statement constructor" do
|
143
|
+
|
144
|
+
before( :each ) do
|
145
|
+
setup_demo_db()
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
it "can be created via a prepared statement" do
|
150
|
+
statement = Kuzu::PreparedStatement.new( connection, <<~END_OF_QUERY )
|
151
|
+
MATCH (u:User)
|
152
|
+
WHERE u.age > $min_age and u.age < $max_age
|
153
|
+
RETURN u.name
|
154
|
+
END_OF_QUERY
|
155
|
+
statement.bind( min_age: 32, max_age: 46 )
|
156
|
+
|
157
|
+
result = described_class.from_prepared_statement( statement )
|
158
|
+
|
159
|
+
expect( result ).to be_a( described_class )
|
160
|
+
expect( result ).to be_success
|
161
|
+
|
162
|
+
result.finish
|
163
|
+
end
|
164
|
+
|
165
|
+
|
166
|
+
it "is automatically finished if constructed with a block" do
|
167
|
+
statement = Kuzu::PreparedStatement.new( connection, <<~END_OF_QUERY )
|
168
|
+
MATCH (u:User)
|
169
|
+
WHERE u.age > $min_age and u.age < $max_age
|
170
|
+
RETURN u.name
|
171
|
+
END_OF_QUERY
|
172
|
+
statement.bind( min_age: 32, max_age: 46 )
|
173
|
+
|
174
|
+
block_result = nil
|
175
|
+
rval = described_class.from_prepared_statement( statement ) do |result|
|
176
|
+
block_result = result
|
177
|
+
expect( result ).to be_a( described_class )
|
178
|
+
expect( result ).to be_success
|
179
|
+
|
180
|
+
result.first['u.name']
|
181
|
+
end
|
182
|
+
|
183
|
+
expect( block_result ).to be_finished
|
184
|
+
expect( rval ).to eq( "Karissa" )
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
188
|
+
|
189
|
+
|
190
|
+
end
|
@@ -0,0 +1,254 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require_relative '../spec_helper'
|
4
|
+
|
5
|
+
require 'kuzu'
|
6
|
+
|
7
|
+
|
8
|
+
RSpec.describe( "data types" ) do
|
9
|
+
|
10
|
+
let( :db ) { Kuzu.database }
|
11
|
+
let( :connection ) { db.connect }
|
12
|
+
|
13
|
+
|
14
|
+
#
|
15
|
+
# Specs
|
16
|
+
#
|
17
|
+
|
18
|
+
it "converts TIMESTAMP values to Time objects" do
|
19
|
+
result = connection.query( %{RETURN timestamp("1970-01-01 00:00:00.004666-10") as x;} )
|
20
|
+
|
21
|
+
expect( result ).to be_a( Kuzu::Result )
|
22
|
+
expect( result ).to be_success
|
23
|
+
|
24
|
+
value = result.first
|
25
|
+
expect( value ).to include( 'x' )
|
26
|
+
|
27
|
+
x = value['x']
|
28
|
+
expect( x ).to be_a( Time )
|
29
|
+
expect( x.year ).to eq( 1970 )
|
30
|
+
expect( x.month ).to eq( 1 )
|
31
|
+
expect( x.day ).to eq( 1 )
|
32
|
+
|
33
|
+
result.finish
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
it "converts STRUCT values to OpenStructs" do
|
38
|
+
result = connection.query( "RETURN {first: 'Adam', last: 'Smith'} AS record;" )
|
39
|
+
|
40
|
+
expect( result ).to be_a( Kuzu::Result )
|
41
|
+
expect( result ).to be_success
|
42
|
+
Kuzu.logger.debug "Result is: %p" % [ result ]
|
43
|
+
|
44
|
+
value = result.first
|
45
|
+
expect( value ).to include( 'record' )
|
46
|
+
|
47
|
+
record = value['record']
|
48
|
+
expect( record ).to be_a( OpenStruct )
|
49
|
+
expect( record.first ).to eq( "Adam" )
|
50
|
+
expect( record.first.encoding ).to eq( Encoding::UTF_8 )
|
51
|
+
expect( record.last ).to eq( "Smith" )
|
52
|
+
expect( record.last.encoding ).to eq( Encoding::UTF_8 )
|
53
|
+
|
54
|
+
result.finish
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
it "converts MAP values to Hashes" do
|
59
|
+
result = connection.query( "RETURN map([1, 2], ['a', 'b']) AS m;" )
|
60
|
+
|
61
|
+
expect( result ).to be_a( Kuzu::Result )
|
62
|
+
expect( result ).to be_success
|
63
|
+
Kuzu.logger.debug "Result is: %p" % [ result ]
|
64
|
+
|
65
|
+
value = result.first
|
66
|
+
expect( value ).to include( 'm' )
|
67
|
+
|
68
|
+
record = value['m']
|
69
|
+
expect( record ).to be_a( Hash )
|
70
|
+
expect( record ).to eq({ 1 => 'a', 2 => 'b' })
|
71
|
+
|
72
|
+
result.finish
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
it "converts LIST values to Arrays" do
|
77
|
+
result = connection.query( 'RETURN ["Alice", "Bob"] AS l;' )
|
78
|
+
|
79
|
+
expect( result ).to be_a( Kuzu::Result )
|
80
|
+
expect( result ).to be_success
|
81
|
+
Kuzu.logger.debug "Result is: %p" % [ result ]
|
82
|
+
|
83
|
+
value = result.first
|
84
|
+
expect( value ).to include( 'l' )
|
85
|
+
|
86
|
+
record = value['l']
|
87
|
+
expect( record ).to be_an( Array )
|
88
|
+
expect( record ).to eq( ['Alice', 'Bob'] )
|
89
|
+
|
90
|
+
result.finish
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
it "converts ARRAY values to Arrays" do
|
95
|
+
result = connection.query( "RETURN CAST([3,4,12,11], 'INT64[4]') as a;" )
|
96
|
+
|
97
|
+
expect( result ).to be_a( Kuzu::Result )
|
98
|
+
expect( result ).to be_success
|
99
|
+
Kuzu.logger.debug "Result is: %p" % [ result ]
|
100
|
+
|
101
|
+
value = result.first
|
102
|
+
expect( value ).to include( 'a' )
|
103
|
+
|
104
|
+
record = value['a']
|
105
|
+
expect( record ).to be_an( Array )
|
106
|
+
expect( record ).to eq( [3, 4, 12, 11] )
|
107
|
+
|
108
|
+
result.finish
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
it "converts ARRAYs of LIST values correctly" do
|
113
|
+
result = connection.query( "RETURN CAST([[5,2,1],[2,3],[15,64,74]], 'INT64[][3]') as a;" )
|
114
|
+
|
115
|
+
expect( result ).to be_a( Kuzu::Result )
|
116
|
+
expect( result ).to be_success
|
117
|
+
Kuzu.logger.debug "Result is: %p" % [ result ]
|
118
|
+
|
119
|
+
value = result.first
|
120
|
+
expect( value ).to include( 'a' )
|
121
|
+
|
122
|
+
record = value['a']
|
123
|
+
expect( record ).to be_an( Array )
|
124
|
+
expect( record ).to eq( [ [5, 2, 1], [2, 3], [15, 64, 74] ] )
|
125
|
+
|
126
|
+
result.finish
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
it "converts NODE values to Kuzu::Node objects" do
|
131
|
+
connection.run( <<~END_OF_SCHEMA )
|
132
|
+
CREATE NODE TABLE Person(id INT64, name STRING, age INT64, PRIMARY KEY(id));
|
133
|
+
COPY Person FROM 'spec/data/test/Person.csv';
|
134
|
+
END_OF_SCHEMA
|
135
|
+
result = connection.query( "MATCH (a:Person) RETURN a;" )
|
136
|
+
|
137
|
+
expect( result ).to be_a( Kuzu::Result )
|
138
|
+
expect( result ).to be_success
|
139
|
+
expect( result.num_tuples ).to eq( 120 )
|
140
|
+
|
141
|
+
result.each do |value|
|
142
|
+
expect( value ).to include( 'a' )
|
143
|
+
|
144
|
+
node = value['a']
|
145
|
+
expect( node ).to be_a( Kuzu::Node )
|
146
|
+
|
147
|
+
expect( node.properties.keys ).to contain_exactly( :id, :name, :age )
|
148
|
+
end
|
149
|
+
|
150
|
+
result.finish
|
151
|
+
end
|
152
|
+
|
153
|
+
|
154
|
+
it "converts REL values to Kuzu::Rel objects" do
|
155
|
+
connection.run( <<~END_OF_SCHEMA )
|
156
|
+
CREATE NODE TABLE Person(id INT64, name STRING, age INT64, PRIMARY KEY(id));
|
157
|
+
CREATE REL TABLE Follows (from Person to Person, since INT64);
|
158
|
+
COPY Person FROM 'spec/data/test/Person.csv';
|
159
|
+
COPY Follows FROM 'spec/data/test/Follows.csv';
|
160
|
+
END_OF_SCHEMA
|
161
|
+
result = connection.query( "MATCH (a:Person)-[r:Follows]->(b:Person) RETURN r;" )
|
162
|
+
|
163
|
+
expect( result ).to be_a( Kuzu::Result )
|
164
|
+
expect( result ).to be_success
|
165
|
+
expect( result.num_tuples ).to eq( 5 )
|
166
|
+
|
167
|
+
result.each do |value|
|
168
|
+
expect( value ).to include( 'r' )
|
169
|
+
|
170
|
+
rel = value['r']
|
171
|
+
expect( rel ).to be_a( Kuzu::Rel )
|
172
|
+
|
173
|
+
expect( rel.src_id ).to_not be_nil
|
174
|
+
expect( rel.dst_id ).to_not be_nil
|
175
|
+
|
176
|
+
expect( rel.properties.keys ).to contain_exactly( :since )
|
177
|
+
end
|
178
|
+
|
179
|
+
result.finish
|
180
|
+
end
|
181
|
+
|
182
|
+
|
183
|
+
it "converts RECURSIVE_REL values to Kuzu::RecursiveRel objects" do
|
184
|
+
connection.run( <<~END_OF_SCHEMA )
|
185
|
+
CREATE NODE TABLE Person(id INT64, name STRING, age INT64, PRIMARY KEY(id));
|
186
|
+
CREATE REL TABLE Follows (from Person to Person, since INT64);
|
187
|
+
COPY Person FROM 'spec/data/test/Person.csv';
|
188
|
+
COPY Follows FROM 'spec/data/test/Follows.csv';
|
189
|
+
END_OF_SCHEMA
|
190
|
+
result = connection.query( <<~END_OF_QUERY )
|
191
|
+
MATCH p = (a:Person)-[:Follows]->(b:Person)
|
192
|
+
WHERE a.name = 'Jake Kling' AND b.name = 'Joaquin Schamberger'
|
193
|
+
RETURN p;
|
194
|
+
END_OF_QUERY
|
195
|
+
|
196
|
+
expect( result ).to be_a( Kuzu::Result )
|
197
|
+
expect( result ).to be_success
|
198
|
+
|
199
|
+
result.each do |value|
|
200
|
+
expect( value ).to include( 'p' )
|
201
|
+
|
202
|
+
rel = value['p']
|
203
|
+
expect( rel ).to be_a( Kuzu::RecursiveRel )
|
204
|
+
|
205
|
+
expect( rel.nodes ).to be_an( Array ).and have_attributes( length: 2 )
|
206
|
+
expect( rel.nodes[0] ).to be_a( Kuzu::Node )
|
207
|
+
expect( rel.nodes[0][:name] ).to eq( 'Jake Kling' )
|
208
|
+
expect( rel.nodes[1] ).to be_a( Kuzu::Node )
|
209
|
+
expect( rel.nodes[1][:name] ).to eq( 'Joaquin Schamberger' )
|
210
|
+
|
211
|
+
expect( rel.rels ).to be_an( Array ).and have_attributes( length: 1 )
|
212
|
+
expect( rel.rels[0][:since] ).to eq( 2012 )
|
213
|
+
end
|
214
|
+
|
215
|
+
result.finish
|
216
|
+
end
|
217
|
+
|
218
|
+
|
219
|
+
it "converts UNIONs to something"
|
220
|
+
|
221
|
+
|
222
|
+
it "converts between nil and NULL" do
|
223
|
+
stmt = connection.prepare( "RETURN $the_value AS value;" )
|
224
|
+
result = stmt.execute( the_value: nil )
|
225
|
+
|
226
|
+
expect( result.first ).to eq( {'value' => nil} )
|
227
|
+
|
228
|
+
result.finish
|
229
|
+
end
|
230
|
+
|
231
|
+
|
232
|
+
it "converts UUIDs to Strings" do
|
233
|
+
result = connection.query( "RETURN UUID('A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11') as u;" )
|
234
|
+
uuid = result.first['u']
|
235
|
+
|
236
|
+
expect( uuid ).to be_a( String )
|
237
|
+
expect( uuid ).to eq( 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11' )
|
238
|
+
|
239
|
+
result.finish
|
240
|
+
end
|
241
|
+
|
242
|
+
|
243
|
+
it "converts DECIMAL types to Float objects" do
|
244
|
+
result = connection.query( %{RETURN CAST(127.3, "DECIMAL(5, 2)") AS d;} )
|
245
|
+
rval = result.first['d']
|
246
|
+
|
247
|
+
expect( rval ).to be_a( Float )
|
248
|
+
expect( rval ).to eq( 127.3 )
|
249
|
+
|
250
|
+
result.finish
|
251
|
+
end
|
252
|
+
|
253
|
+
|
254
|
+
end
|
data/spec/kuzu_spec.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
#!/usr/bin/env ruby -S rspec
|
2
|
+
|
3
|
+
require_relative 'spec_helper'
|
4
|
+
require 'kuzu'
|
5
|
+
|
6
|
+
|
7
|
+
RSpec.describe( Kuzu ) do
|
8
|
+
|
9
|
+
let( :spec_tmpdir ) do
|
10
|
+
tmpfile_pathname()
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
it "should have a VERSION constant" do
|
15
|
+
expect( subject.const_get('VERSION') ).to_not be_empty
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
it "knows what version of Kuzu it's linked with" do
|
20
|
+
expect( described_class.kuzu_version ).to match( /\A\d+\.\d+\.\d+/ )
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
it "knows what version of the storage scheme it's using" do
|
25
|
+
result = described_class.storage_version
|
26
|
+
|
27
|
+
expect( result ).to be_an( Integer )
|
28
|
+
expect( result ).to be > 0
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
it "can construct an in-memory database with reasonable defaults" do
|
33
|
+
expect( described_class.database ).to be_a( Kuzu::Database )
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
it "can construct an in-memory database with a nil path" do
|
38
|
+
expect( described_class.database(nil) ).to be_a( Kuzu::Database )
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
it "can construct an in-memory database explicitly" do
|
43
|
+
expect( described_class.database(:memory) ).to be_a( Kuzu::Database )
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
it "can construct a on-disk database with reasonable defaults" do
|
48
|
+
filename = spec_tmpdir + 'spec_db'
|
49
|
+
result = described_class.database( filename )
|
50
|
+
|
51
|
+
expect( result ).to be_a( Kuzu::Database )
|
52
|
+
expect( filename ).to be_a_directory
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|