Merge pull request 'multi-result-sets' (#1) from multi-result-sets into main
This commit is contained in:
commit
02894cf8fe
12 changed files with 141 additions and 1284 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -6,4 +6,5 @@ kuzu
|
||||||
nimcache/*
|
nimcache/*
|
||||||
tests/*
|
tests/*
|
||||||
testresults/*
|
testresults/*
|
||||||
|
testresults.html
|
||||||
|
|
||||||
|
|
|
||||||
15
History.md
15
History.md
|
|
@ -1,5 +1,19 @@
|
||||||
# Release History for nim-kuzu
|
# Release History for nim-kuzu
|
||||||
|
|
||||||
|
---
|
||||||
|
## v0.6.0 [2025-07-17] Mahlon E. Smith <mahlon@martini.nu>
|
||||||
|
|
||||||
|
Enhancement:
|
||||||
|
|
||||||
|
- Provide functions for retreiving multiple result sets from a
|
||||||
|
single primary query object.
|
||||||
|
|
||||||
|
|
||||||
|
Minutiae:
|
||||||
|
|
||||||
|
- Add a KuzuInterationError, to disambiguate iteration errors from
|
||||||
|
IndexErrors.
|
||||||
|
|
||||||
---
|
---
|
||||||
## v0.5.0 [2025-07-13] Mahlon E. Smith <mahlon@martini.nu>
|
## v0.5.0 [2025-07-13] Mahlon E. Smith <mahlon@martini.nu>
|
||||||
|
|
||||||
|
|
@ -34,4 +48,3 @@ so update version to reflect the 0.9.0 release.
|
||||||
## v0.1.0 [2025-03-31] Mahlon E. Smith <mahlon@martini.nu>
|
## v0.1.0 [2025-03-31] Mahlon E. Smith <mahlon@martini.nu>
|
||||||
|
|
||||||
Initial public release.
|
Initial public release.
|
||||||
|
|
||||||
|
|
|
||||||
46
USAGE.md
46
USAGE.md
|
|
@ -308,13 +308,13 @@ for pair in res.column_names.zip( res.column_types ):
|
||||||
# ("e.activity", KUZU_TIMESTAMP)
|
# ("e.activity", KUZU_TIMESTAMP)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Reading Result Sets
|
## Reading Results
|
||||||
|
|
||||||
So far we've just been showing values by converting the entire `KuzuQueryResult`
|
So far we've just been showing values by converting the entire `KuzuQueryResult`
|
||||||
to a string. Convenient for quick examples and debugging, but not much else.
|
to a string. Convenient for quick examples and debugging, but not much else.
|
||||||
|
|
||||||
A `KuzuQueryResult` is an iterator. You can use regular Nim functions that yield
|
A `KuzuQueryResult` is an iterator. You can use regular Nim functions that yield
|
||||||
each `KuzuFlatTuple` -- essentially, each row that was returned in the set.
|
each `KuzuFlatTuple` -- essentially, each row that was returned in a result set.
|
||||||
|
|
||||||
```nim
|
```nim
|
||||||
var res = conn.query """
|
var res = conn.query """
|
||||||
|
|
@ -350,12 +350,52 @@ if res.hasNext:
|
||||||
|
|
||||||
echo res.getNext #=> 2
|
echo res.getNext #=> 2
|
||||||
echo res.getNext #=> 3
|
echo res.getNext #=> 3
|
||||||
echo res.getNext #=> KuzuIndexError exception!
|
echo res.getNext #=> KuzuIterationError exception!
|
||||||
```
|
```
|
||||||
|
|
||||||
Manually rewind the `KuzuQueryResult` via `rewind()`.
|
Manually rewind the `KuzuQueryResult` via `rewind()`.
|
||||||
|
|
||||||
|
|
||||||
|
## Multiple Query Results
|
||||||
|
|
||||||
|
A query can potentially return any number of separate statements. Iterate over
|
||||||
|
linked `KuzuQueryResult` objects with the `sets()` iterator.
|
||||||
|
|
||||||
|
```nim
|
||||||
|
import kuzu
|
||||||
|
|
||||||
|
let db = newKuzuDatabase()
|
||||||
|
let conn = db.connect
|
||||||
|
|
||||||
|
let query = conn.query """
|
||||||
|
UNWIND [1,2,3] as items
|
||||||
|
RETURN items;
|
||||||
|
|
||||||
|
UNWIND [4,5,6] as items
|
||||||
|
RETURN items;
|
||||||
|
|
||||||
|
UNWIND [7,8,9] as items
|
||||||
|
RETURN items;
|
||||||
|
"""
|
||||||
|
|
||||||
|
for row in query:
|
||||||
|
echo row
|
||||||
|
# 1
|
||||||
|
# 2
|
||||||
|
# 3
|
||||||
|
|
||||||
|
for set in query.sets:
|
||||||
|
for row in set:
|
||||||
|
echo row
|
||||||
|
# 4
|
||||||
|
# 5
|
||||||
|
# 6
|
||||||
|
# 7
|
||||||
|
# 8
|
||||||
|
# 9
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## Working with Values
|
## Working with Values
|
||||||
|
|
||||||
A `KuzuFlatTuple` contains the entire row. You can index a value at its column
|
A `KuzuFlatTuple` contains the entire row. You can index a value at its column
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ import
|
||||||
std/strformat
|
std/strformat
|
||||||
import kuzu
|
import kuzu
|
||||||
|
|
||||||
const DB = "imdb"
|
const DB = "imdb.kz"
|
||||||
const DOT = "imdb-results.dot"
|
const DOT = "imdb-results.dot"
|
||||||
|
|
||||||
if not DB.fileExists:
|
if not DB.fileExists:
|
||||||
|
|
@ -37,7 +37,7 @@ var res: KuzuQueryResult
|
||||||
var fromActor = paramStr(1)
|
var fromActor = paramStr(1)
|
||||||
var toActor = paramStr(2)
|
var toActor = paramStr(2)
|
||||||
|
|
||||||
var db = newKuzuDatabase( "imdb", kuzuConfig(read_only=true) )
|
var db = newKuzuDatabase( DB, kuzuConfig(read_only=true) )
|
||||||
var conn = db.connect
|
var conn = db.connect
|
||||||
|
|
||||||
echo "Database opened: ", db.path
|
echo "Database opened: ", db.path
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import
|
||||||
zip/gzipfiles,
|
zip/gzipfiles,
|
||||||
kuzu
|
kuzu
|
||||||
|
|
||||||
const DB = "imdb"
|
const DB = "imdb.kz"
|
||||||
const SOURCE = "https://datasets.imdbws.com"
|
const SOURCE = "https://datasets.imdbws.com"
|
||||||
const FILES = @[ "name.basics", "title.basics", "title.principals" ]
|
const FILES = @[ "name.basics", "title.basics", "title.principals" ]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# vim: set et sta sw=4 ts=4 :
|
# vim: set et sta sw=4 ts=4 :
|
||||||
|
|
||||||
version = "0.5.0"
|
version = "0.6.0"
|
||||||
author = "Mahlon E. Smith"
|
author = "Mahlon E. Smith"
|
||||||
description = "Kuzu is an embedded graph database built for query speed and scalability."
|
description = "Kuzu is an embedded graph database built for query speed and scalability."
|
||||||
license = "BSD-3-Clause"
|
license = "BSD-3-Clause"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# vim: set et sta sw=4 ts=4 :
|
# vim: set et sta sw=4 ts=4 :
|
||||||
|
|
||||||
const KUZU_VERSION* = "0.5.0"
|
const KUZU_VERSION* = "0.6.0"
|
||||||
const KUZU_EXPECTED_LIBVERSION* = "0.11.0"
|
const KUZU_EXPECTED_LIBVERSION* = "0.11.0"
|
||||||
const BLOB_MAXSIZE = 4096
|
const BLOB_MAXSIZE = 4096
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -147,14 +147,14 @@ func hasNext*( query: KuzuQueryResult ): bool =
|
||||||
|
|
||||||
|
|
||||||
func getNext*( query: KuzuQueryResult ): KuzuFlatTuple =
|
func getNext*( query: KuzuQueryResult ): KuzuFlatTuple =
|
||||||
## Consume and return the next tuple result, or raise a KuzuIndexError
|
## Consume and return the next tuple result, or raise a KuzuIterationError
|
||||||
## if at the end of the result set.
|
## if at the end of the result tuples.
|
||||||
result = new KuzuFlatTuple
|
result = new KuzuFlatTuple
|
||||||
if kuzu_query_result_get_next( addr query.handle, addr result.handle ) == KuzuSuccess:
|
if kuzu_query_result_get_next( addr query.handle, addr result.handle ) == KuzuSuccess:
|
||||||
result.valid = true
|
result.valid = true
|
||||||
result.num_columns = query.num_columns
|
result.num_columns = query.num_columns
|
||||||
else:
|
else:
|
||||||
raise newException( KuzuIndexError, &"Query iteration past end." )
|
raise newException( KuzuIterationError, &"Query iteration past end of tuples." )
|
||||||
|
|
||||||
|
|
||||||
func rewind*( query: KuzuQueryResult ) =
|
func rewind*( query: KuzuQueryResult ) =
|
||||||
|
|
@ -162,6 +162,32 @@ func rewind*( query: KuzuQueryResult ) =
|
||||||
kuzu_query_result_reset_iterator( addr query.handle )
|
kuzu_query_result_reset_iterator( addr query.handle )
|
||||||
|
|
||||||
|
|
||||||
|
func hasNextSet*( query: KuzuQueryResult ): bool =
|
||||||
|
## Returns +true+ if there are more result sets to be consumed.
|
||||||
|
result = kuzu_query_result_has_next_query_result( addr query.handle )
|
||||||
|
|
||||||
|
|
||||||
|
# Keeping this private, because it's only safe to call from within
|
||||||
|
# an iterator -- overwriting a query variable with the next result
|
||||||
|
# goes boom.
|
||||||
|
func getNextSet( query: KuzuQueryResult ): KuzuQueryResult =
|
||||||
|
## Consume and return the next query set result, or raise a KuzuIterationError
|
||||||
|
## if at the end of sets.
|
||||||
|
result = new KuzuQueryResult
|
||||||
|
if kuzu_query_result_get_next_query_result( addr query.handle, addr result.handle ) == KuzuSuccess:
|
||||||
|
result.valid = true
|
||||||
|
result.getQueryMetadata()
|
||||||
|
else:
|
||||||
|
raise newException( KuzuIterationError, &"Query iteration past end of set." )
|
||||||
|
|
||||||
|
|
||||||
|
iterator sets*( query: KuzuQueryResult ): KuzuQueryResult =
|
||||||
|
## Iterate available query result sets, yielding to the block.
|
||||||
|
while query.hasNextSet:
|
||||||
|
yield query.getNextSet
|
||||||
|
# NOTE: There is no 'rewind' mechanism for result sets.
|
||||||
|
|
||||||
|
|
||||||
iterator items*( query: KuzuQueryResult ): KuzuFlatTuple =
|
iterator items*( query: KuzuQueryResult ): KuzuFlatTuple =
|
||||||
## Iterate available tuples, yielding to the block.
|
## Iterate available tuples, yielding to the block.
|
||||||
while query.hasNext:
|
while query.hasNext:
|
||||||
|
|
|
||||||
|
|
@ -51,5 +51,6 @@ type
|
||||||
KuzuException* = object of CatchableError
|
KuzuException* = object of CatchableError
|
||||||
KuzuQueryError* = object of KuzuException
|
KuzuQueryError* = object of KuzuException
|
||||||
KuzuIndexError* = object of KuzuException
|
KuzuIndexError* = object of KuzuException
|
||||||
|
KuzuIterationError* = object of KuzuException
|
||||||
KuzuTypeError* = object of KuzuException
|
KuzuTypeError* = object of KuzuException
|
||||||
|
|
||||||
|
|
|
||||||
1266
testresults.html
1266
testresults.html
File diff suppressed because it is too large
Load diff
42
tests/queries/t_can_contain_multiple_result_sets.nim
Normal file
42
tests/queries/t_can_contain_multiple_result_sets.nim
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
# vim: set et sta sw=4 ts=4 :
|
||||||
|
|
||||||
|
discard """
|
||||||
|
output: "Jenny|Lenny\nLenny\nJenny\n"
|
||||||
|
"""
|
||||||
|
|
||||||
|
import kuzu
|
||||||
|
|
||||||
|
let db = newKuzuDatabase()
|
||||||
|
let conn = db.connect
|
||||||
|
|
||||||
|
var q = conn.query """
|
||||||
|
CREATE NODE TABLE User(
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
name STRING
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE REL TABLE FOLLOWS(
|
||||||
|
From User To User
|
||||||
|
);
|
||||||
|
|
||||||
|
MERGE (a:User {name: "Lenny"})-[f:Follows]->(b:User {name: "Jenny"});
|
||||||
|
"""
|
||||||
|
|
||||||
|
q = conn.query( "MATCH (u:User) RETURN *" )
|
||||||
|
|
||||||
|
assert typeOf( q ) is KuzuQueryResult
|
||||||
|
assert q.hasNextSet == false
|
||||||
|
|
||||||
|
q = conn.query """
|
||||||
|
MATCH (a:User)<-[f:Follows]-(b:User) RETURN a.name, b.name;
|
||||||
|
MATCH (u:User) RETURN u.name;
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert typeOf( q ) is KuzuQueryResult
|
||||||
|
assert q.hasNextSet == true
|
||||||
|
|
||||||
|
echo q.getNext
|
||||||
|
for query_result in q.sets:
|
||||||
|
for row in query_result.items:
|
||||||
|
echo row
|
||||||
|
|
||||||
|
|
@ -12,6 +12,6 @@ q = conn.query( "MATCH (d:Doop) RETURN d.thing" )
|
||||||
|
|
||||||
try:
|
try:
|
||||||
discard q.getNext
|
discard q.getNext
|
||||||
except KuzuIndexError as err:
|
except KuzuIterationError as err:
|
||||||
assert err.msg.contains( re"""Query iteration past end.""" )
|
assert err.msg.contains( re"""Query iteration past end.""" )
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue