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/*
|
||||
tests/*
|
||||
testresults/*
|
||||
testresults.html
|
||||
|
||||
|
|
|
|||
17
History.md
17
History.md
|
|
@ -1,5 +1,19 @@
|
|||
# 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>
|
||||
|
||||
|
|
@ -17,7 +31,7 @@ Bump for kuzu 0.10.0. No practical changes.
|
|||
|
||||
Enhancement:
|
||||
|
||||
- Add `toBlob` for quickly fetching a sequence of opaque bytes.
|
||||
- Add `toBlob` for quickly fetching a sequence of opaque bytes.
|
||||
|
||||
|
||||
---
|
||||
|
|
@ -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>
|
||||
|
||||
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)
|
||||
```
|
||||
|
||||
## Reading Result Sets
|
||||
## Reading Results
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
var res = conn.query """
|
||||
|
|
@ -350,12 +350,52 @@ if res.hasNext:
|
|||
|
||||
echo res.getNext #=> 2
|
||||
echo res.getNext #=> 3
|
||||
echo res.getNext #=> KuzuIndexError exception!
|
||||
echo res.getNext #=> KuzuIterationError exception!
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
A `KuzuFlatTuple` contains the entire row. You can index a value at its column
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import
|
|||
std/strformat
|
||||
import kuzu
|
||||
|
||||
const DB = "imdb"
|
||||
const DB = "imdb.kz"
|
||||
const DOT = "imdb-results.dot"
|
||||
|
||||
if not DB.fileExists:
|
||||
|
|
@ -37,7 +37,7 @@ var res: KuzuQueryResult
|
|||
var fromActor = paramStr(1)
|
||||
var toActor = paramStr(2)
|
||||
|
||||
var db = newKuzuDatabase( "imdb", kuzuConfig(read_only=true) )
|
||||
var db = newKuzuDatabase( DB, kuzuConfig(read_only=true) )
|
||||
var conn = db.connect
|
||||
|
||||
echo "Database opened: ", db.path
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import
|
|||
zip/gzipfiles,
|
||||
kuzu
|
||||
|
||||
const DB = "imdb"
|
||||
const DB = "imdb.kz"
|
||||
const SOURCE = "https://datasets.imdbws.com"
|
||||
const FILES = @[ "name.basics", "title.basics", "title.principals" ]
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
author = "Mahlon E. Smith"
|
||||
description = "Kuzu is an embedded graph database built for query speed and scalability."
|
||||
license = "BSD-3-Clause"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# 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 BLOB_MAXSIZE = 4096
|
||||
|
||||
|
|
|
|||
|
|
@ -147,14 +147,14 @@ func hasNext*( query: KuzuQueryResult ): bool =
|
|||
|
||||
|
||||
func getNext*( query: KuzuQueryResult ): KuzuFlatTuple =
|
||||
## Consume and return the next tuple result, or raise a KuzuIndexError
|
||||
## if at the end of the result set.
|
||||
## Consume and return the next tuple result, or raise a KuzuIterationError
|
||||
## if at the end of the result tuples.
|
||||
result = new KuzuFlatTuple
|
||||
if kuzu_query_result_get_next( addr query.handle, addr result.handle ) == KuzuSuccess:
|
||||
result.valid = true
|
||||
result.num_columns = query.num_columns
|
||||
else:
|
||||
raise newException( KuzuIndexError, &"Query iteration past end." )
|
||||
raise newException( KuzuIterationError, &"Query iteration past end of tuples." )
|
||||
|
||||
|
||||
func rewind*( query: KuzuQueryResult ) =
|
||||
|
|
@ -162,6 +162,32 @@ func rewind*( query: KuzuQueryResult ) =
|
|||
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 =
|
||||
## Iterate available tuples, yielding to the block.
|
||||
while query.hasNext:
|
||||
|
|
|
|||
|
|
@ -51,5 +51,6 @@ type
|
|||
KuzuException* = object of CatchableError
|
||||
KuzuQueryError* = object of KuzuException
|
||||
KuzuIndexError* = object of KuzuException
|
||||
KuzuIterationError* = 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:
|
||||
discard q.getNext
|
||||
except KuzuIndexError as err:
|
||||
except KuzuIterationError as err:
|
||||
assert err.msg.contains( re"""Query iteration past end.""" )
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue