# KuzuAlchemy
A SQLAlchemy-like ORM for Kuzu graph database
<!-- KUZUALCHEMY-AUTO-UPDATE-START -->
# Version: 0.2.0
**Status**: Alpha
**Tests**: ⏳ Running tests... (Last updated: 2025-09-04 18:35:11 UTC)
[](https://github.com/FanaticPythoner/kuzualchemy/actions/workflows/test.yml)
[](https://badge.fury.io/py/kuzualchemy)
[](https://pypi.org/project/kuzualchemy/)
KuzuAlchemy is an Object-Relational Mapping (ORM) library for the [Kuzu graph database](https://kuzudb.com/). It provides a SQLAlchemy-like interface for working with graph data.
> **Note**: This software is currently in alpha development. APIs may change.
<!-- KUZUALCHEMY-AUTO-UPDATE-END -->
## Table of Contents
1. [Overview](#overview)
2. [Installation](#installation)
3. [Quick Start](#quick-start)
4. [Function Reference](#function-reference)
5. [Operator Reference](#operator-reference)
6. [Model Definition](#model-definition)
7. [Field Types & Metadata](#field-types--metadata)
8. [Relationships](#relationships)
9. [Query System](#query-system)
10. [Session Management](#session-management)
11. [Advanced Features](#advanced-features)
12. [API Reference](#api-reference)
13. [Contributing](#contributing)
14. [License](#license)
## Overview
KuzuAlchemy provides the following components:
- **Core ORM** (`kuzu_orm.py`): Base classes for nodes and relationships with metadata handling
- **Session Management** (`kuzu_session.py`): Database operations with transaction support
- **Query System** (`kuzu_query.py`): Query builder with Cypher generation
- **Expression Engine** (`kuzu_query_expressions.py`): Expression system supporting Kuzu operators
- **Function Library** (`kuzu_functions.py`): Kuzu functions implemented as standalone callables
- **Field Integration** (`kuzu_query_fields.py`): QueryField methods providing fluent API access to functions
### Key Features
- **Kuzu Function Support**: Kuzu functions and operators implemented
- **ORM**: Model definition, session management, and querying capabilities
- **Type-Safe Operations**: Type safety with parameter handling and validation
- **Testing**: Test coverage for functionality
- **Error Handling**: Error handling and transaction management
## Installation
### Prerequisites
```bash
pip install kuzu pydantic
```
### Install KuzuAlchemy
```bash
pip install kuzualchemy
```
### Development Installation
```bash
git clone <repository-url>
cd kuzualchemy
pip install -e ".[dev,test]"
```
## Quick Start
### Basic Setup
```python
from kuzualchemy import (
KuzuBaseModel, KuzuRelationshipBase,
kuzu_node, kuzu_relationship, kuzu_field,
KuzuDataType, KuzuSession,
get_all_ddl
)
# Create session
session = KuzuSession(db_path="database.db")
# Initialize schema
ddl = get_all_ddl()
if ddl.strip():
session.execute(ddl)
```
### Example
```python
import kuzualchemy as ka
from pathlib import Path
# Define your graph models
@ka.kuzu_node("Person")
class Person(ka.KuzuBaseModel):
name: str = ka.kuzu_field(kuzu_type=ka.KuzuDataType.STRING, primary_key=True)
age: int = ka.kuzu_field(kuzu_type=ka.KuzuDataType.INT32)
email: str = ka.kuzu_field(kuzu_type=ka.KuzuDataType.STRING)
@ka.kuzu_relationship("KNOWS", pairs=[(Person, Person)])
class Knows(ka.KuzuRelationshipBase):
since: int = ka.kuzu_field(kuzu_type=ka.KuzuDataType.INT32)
strength: float = ka.kuzu_field(kuzu_type=ka.KuzuDataType.DOUBLE, default=1.0)
# Create database and session
db_path = Path("my_graph.db")
session = ka.KuzuSession(db_path)
# Create schema
session.execute(ka.get_all_ddl())
# Insert data
alice = Person(name="Alice", age=30, email="alice@example.com")
bob = Person(name="Bob", age=25, email="bob@example.com")
knows = Knows(from_node=alice, to_node=bob, since=2020, strength=0.9)
# Or, you could do `session.add_all([alice, bob, knows])`
session.add(alice)
session.add(bob)
session.add(knows)
session.commit()
# Query data
query = ka.Query(Person, session=session)
filtered_query = query.where(query.fields.age > 25)
results = filtered_query.all()
print(f"Found {len(results)} people over 25")
```
---
## Function Reference
KuzuAlchemy implements Kuzu functions across multiple categories. Each function returns a `FunctionExpression` object that can be used in queries and expressions.
### Text Functions
String manipulation and text processing functions:
| Function | Description | kuzu_functions | QueryField |
|----------|-------------|----------------|------------|
| `concat(*args)` | Concatenate multiple strings | ✓ | ✓ |
| `ws_concat(separator, *args)` | Concatenate strings with separator | ✓ | — |
| `array_extract(string_or_list, index)` | Extract element at 1-based index from string or list | ✓ | — |
| `array_slice(string_or_list, begin, end)` | Slice string or list (1-based) | ✓ | — |
| `list_element(list_value, index)` | Extract list element at index | ✓ | ✓ |
| `list_extract(list_value, index)` | Extract list element at index (alias) | ✓ | ✓ |
| `contains(string1, string2)` | Substring test | ✓ | ✓ |
| `ends_with(string1, string2)` | Ends-with test (alias of suffix) | ✓ | ✓ |
| `lower(string)` | Lowercase | ✓ | ✓ |
| `lcase(string)` | Lowercase (alias) | ✓ | ✓ |
| `left(string, count)` | Left substring | ✓ | ✓ |
| `levenshtein(s1, s2)` | Edit distance | ✓ | ✓ |
| `lpad(string, count, character)` | Left pad | ✓ | ✓ |
| `ltrim(string)` | Trim left | ✓ | ✓ |
| `prefix(string, search_string)` | Starts-with test | ✓ | — |
| `repeat(string, count)` | Repeat string | ✓ | ✓ |
| `reverse(string)` | Reverse string | ✓ | ✓ |
| `right(string, count)` | Right substring | ✓ | ✓ |
| `rpad(string, count, character)` | Right pad | ✓ | ✓ |
| `rtrim(string)` | Trim right | ✓ | ✓ |
| `starts_with(string1, string2)` | Starts-with test (alias of prefix) | ✓ | ✓ |
| `substring(string, start, length)` | Substring by 1-based start/length | ✓ | ✓ |
| `substr(string, start, length)` | Substring (alias) | ✓ | ✓ |
| `suffix(string, search_string)` | Ends-with test | ✓ | — |
| `trim(string)` | Trim both sides | ✓ | ✓ |
| `upper(string)` | Uppercase | ✓ | ✓ |
| `ucase(string)` | Uppercase (alias) | ✓ | ✓ |
| `initcap(string)` | Capitalize first letter | ✓ | ✓ |
| `string_split(string, separator)` | Split to array | ✓ | ✓ |
| `split_part(string, separator, index)` | Part at 1-based index | ✓ | ✓ |
### Pattern Matching Functions
Regular expression utilities:
| Function | Description | kuzu_functions | QueryField |
|----------|-------------|----------------|------------|
| `regexp_matches(string, pattern)` | Regex test | ✓ | ✓ |
| `regexp_replace(string, pattern, replacement[, options])` | Regex replace | ✓ | ✓ |
| `regexp_extract(string, pattern[, group])` | Extract first match/group | ✓ | ✓ |
| `regexp_extract_all(string, pattern[, group])` | Extract all matches/groups | ✓ | ✓ |
| `regexp_split_to_array(string, pattern[, options])` | Split by regex | ✓ | ✓ |
### List Functions
Array and list manipulation functions:
| Function | Description | kuzu_functions | QueryField |
|----------|-------------|----------------|------------|
| `list_creation(...)` | Create a list containing the argument values | ✓ | — |
| `size(value)` | Return size of string or list | ✓ | ✓ |
| `list_concat(list1, list2)` | Concatenate two lists | ✓ | ✓ |
| `range(start, stop, [step])` | Return list from start to stop with step | ✓ | — |
| `list_cat(list1, list2)` | Alias of list_concat | ✓ | ✓ |
| `array_concat(list1, list2)` | Alias of list_concat | ✓ | ✓ |
| `array_cat(list1, list2)` | Alias of list_concat | ✓ | ✓ |
| `list_append(list, element)` | Append element to list | ✓ | ✓ |
| `array_append(list, element)` | Alias of list_append | ✓ | ✓ |
| `array_push_back(list, element)` | Alias of list_append | ✓ | ✓ |
| `list_prepend(list, element)` | Prepend element to list | ✓ | ✓ |
| `array_prepend(list, element)` | Alias of list_prepend | ✓ | ✓ |
| `array_push_front(list, element)` | Alias of list_prepend | ✓ | ✓ |
| `list_position(list, element)` | Position of element in list | ✓ | ✓ |
| `list_indexof(list, element)` | Alias of list_position | ✓ | ✓ |
| `array_position(list, element)` | Alias of list_position | ✓ | ✓ |
| `array_indexof(list, element)` | Alias of list_position | ✓ | ✓ |
| `list_contains(list, element)` | Check if list contains element | ✓ | ✓ |
| `list_has(list, element)` | Alias of list_contains | ✓ | ✓ |
| `array_contains(list, element)` | Alias of list_contains | ✓ | ✓ |
| `array_has(list, element)` | Alias of list_contains | ✓ | ✓ |
| `list_slice(list, begin, end)` | Extract sub-list | ✓ | ✓ |
### Advanced List Functions
Higher-order and quantifier list functions (order matches kuzu_functions.py):
| Function | Description | kuzu_functions | QueryField |
|----------|-------------|----------------|------------|
| `list_reverse(list)` | Reverse list elements | ✓ | ✓ |
| `list_sort(list[, order, nulls])` | Sort elements of list | ✓ | ✓ |
| `list_reverse_sort(list)` | Sort elements of list in DESC | ✓ | ✓ |
| `list_sum(list)` | Sum elements | ✓ | ✓ |
| `list_product(list)` | Multiply elements | ✓ | ✓ |
| `list_distinct(list)` | Remove NULLs and duplicates | ✓ | ✓ |
| `list_unique(list)` | Count unique elements | ✓ | ✓ |
| `list_any_value(list)` | First non-NULL value | ✓ | ✓ |
| `list_to_string(sep, list)` | Join elements with separator | ✓ | ✓ |
| `list_transform(list, lambda)` | Transform elements using lambda expression | ✓ | ✓ |
| `list_filter(list, lambda)` | Filter elements using lambda expression | ✓ | ✓ |
| `list_reduce(list, lambda)` | Reduce list using lambda expression | ✓ | ✓ |
| `list_has_all(list, sub_list)` | Contains all elements from sub-list | ✓ | ✓ |
| `all_func(var, list, predicate)` | All elements satisfy predicate | ✓ | ✓ |
| `any_func(var, list, predicate)` | Any element satisfies predicate | ✓ | ✓ |
| `none_func(var, list, predicate)` | No elements satisfy predicate | ✓ | ✓ |
| `single_func(var, list, predicate)` | Exactly one element satisfies predicate | ✓ | ✓ |
| `array_slice(array, start, end)` | Slice array | `ka.array_slice(field, 1, 5)` |
| `list_reverse(list)` | Reverse list | `ka.list_reverse(field)` |
| `list_sort(list)` | Sort list | `ka.list_sort(field)` |
| `list_reverse_sort(list)` | Reverse sort | `ka.list_reverse_sort(field)` |
| `list_sum(list)` | Sum elements | `ka.list_sum(field)` |
| `list_product(list)` | Product elements | `ka.list_product(field)` |
| `list_distinct(list)` | Distinct elements | `ka.list_distinct(field)` |
| `list_unique(list)` | Unique elements | `ka.list_unique(field)` |
| `list_any_value(list)` | Any element | `ka.list_any_value(field)` |
| `list_to_string(sep, list)` | Join elements | `ka.list_to_string(",", field)` |
| `list_extract(list, index)` | Extract element | `ka.list_extract(field, 1)` |
| `list_element(list, index)` | Get element | `ka.list_element(field, 1)` |
| `range(start, end, [step])` | Generate range | `ka.range(1, 10)` |
### Numeric Functions
Mathematical and numeric computation functions:
| Function | Description | kuzu_functions | QueryField |
|----------|-------------|----------------|------------|
| `pi()` | Return value of pi | ✓ | — |
| `abs(value)` | Absolute value | ✓ | ✓ |
| `ceil(value)` | Ceiling | ✓ | ✓ |
| `ceiling(value)` | Ceiling (alias) | ✓ | ✓ |
| `floor(value)` | Floor | ✓ | ✓ |
| `round(value, precision=0)` | Round to precision | ✓ | ✓ |
| `sqrt(value)` | Square root | ✓ | ✓ |
| `pow(base, exponent)` | Power | ✓ | ✓ |
| `sin(value)` | Sine | ✓ | ✓ |
| `cos(value)` | Cosine | ✓ | ✓ |
| `tan(value)` | Tangent | ✓ | ✓ |
| `asin(value)` | Arcsine | ✓ | ✓ |
| `acos(value)` | Arccosine | ✓ | ✓ |
| `atan(value)` | Arctangent | ✓ | ✓ |
| `atan2(x, y)` | Arctangent of x,y | ✓ | ✓ |
| `ln(value)` | Natural log | ✓ | ✓ |
| `log(value)` | Logarithm | ✓ | ✓ |
| `log2(value)` | Base-2 logarithm | ✓ | ✓ |
| `log10(value)` | Base-10 logarithm | ✓ | ✓ |
| `negate(value)` | Negation | ✓ | ✓ |
| `sign(value)` | Sign (-1,0,1) | ✓ | ✓ |
| `even(value)` | Round to next even | ✓ | ✓ |
| `factorial(value)` | Factorial | ✓ | ✓ |
| `gamma(value)` | Gamma function | ✓ | ✓ |
| `lgamma(value)` | Log Gamma | ✓ | ✓ |
| `bitwise_xor(x, y)` | Bitwise XOR | ✓ | ✓ |
| `cot(value)` | Cotangent | ✓ | ✓ |
| `degrees(value)` | Radians to degrees | ✓ | ✓ |
| `radians(value)` | Degrees to radians | ✓ | ✓ |
### Date Functions
Date manipulation and extraction functions:
| Function | Description | kuzu_functions | QueryField |
|----------|-------------|----------------|------------|
| `current_date()` | Current date | ✓ | — |
| `current_timestamp()` | Current timestamp | ✓ | — |
| `date_part(part, date)` | Extract date part | ✓ | ✓ |
| `date_trunc(part, date)` | Truncate date | ✓ | ✓ |
| `datepart(part, date)` | Extract date part (alias) | ✓ | ✓ |
| `datetrunc(part, date)` | Truncate date (alias) | ✓ | ✓ |
| `dayname(date)` | Day name | ✓ | ✓ |
| `monthname(date)` | Month name | ✓ | ✓ |
| `last_day(date)` | Last day of month | ✓ | ✓ |
| `greatest(...)` | Greatest of values | ✓ | ✓ |
| `least(...)` | Least of values | ✓ | ✓ |
| `make_date(year, month, day)` | Create date | ✓ | ✓ |
### Timestamp Functions
Timestamp manipulation and extraction functions:
| Function | Description | kuzu_functions | QueryField |
|----------|-------------|----------------|------------|
| `century(timestamp)` | Extract century | ✓ | ✓ |
| `epoch_ms(ms)` | Convert milliseconds to timestamp | ✓ | — |
| `to_epoch_ms(timestamp)` | Convert timestamp to milliseconds | ✓ | ✓ |
### Interval Functions
Interval manipulation and conversion functions:
| Function | Description | kuzu_functions | QueryField |
|----------|-------------|----------------|------------|
| `to_years(value)` | Convert integer to year interval | ✓ | ✓ |
| `to_months(value)` | Convert integer to month interval | ✓ | ✓ |
| `to_days(value)` | Convert integer to day interval | ✓ | ✓ |
| `to_hours(value)` | Convert integer to hour interval | ✓ | ✓ |
| `to_minutes(value)` | Convert integer to minute interval | ✓ | ✓ |
| `to_seconds(value)` | Convert integer to second interval | ✓ | ✓ |
| `to_milliseconds(value)` | Convert integer to millisecond interval | ✓ | ✓ |
| `to_microseconds(value)` | Convert integer to microsecond interval | ✓ | ✓ |
### Timestamp Functions
Timestamp manipulation and extraction functions (order matches kuzu_functions.py):
| Function | Description | kuzu_functions | QueryField |
|----------|-------------|----------------|------------|
| `century(timestamp)` | Extract century | ✓ | ✓ |
| `epoch_ms(ms)` | Convert milliseconds to timestamp | ✓ | — |
| `to_epoch_ms(timestamp)` | Convert timestamp to milliseconds | ✓ | ✓ |
### Interval Functions
Interval manipulation and conversion functions:
### Map Functions
Map manipulation and access functions:
| Function | Description | kuzu_functions | QueryField |
|----------|-------------|----------------|------------|
| `map_func(keys, values)` | Create map from keys and values | ✓ | — |
| `map_extract(map, key)` | Extract value for key | ✓ | ✓ |
| `element_at(map, key)` | Extract value (alias) | ✓ | ✓ |
| `cardinality(map)` | Map size | ✓ | ✓ |
| `map_keys(map)` | Get all keys | ✓ | ✓ |
| `map_values(map)` | Get all values | ✓ | ✓ |
### Union Functions
Union type manipulation functions:
| Function | Description | kuzu_functions | QueryField |
|----------|-------------|----------------|------------|
| `union_value(tag := value)` | Create union with tag/value | ✓ | — |
| `union_tag(union)` | Get union tag | ✓ | ✓ |
| `union_extract(union, tag)` | Extract value for tag | ✓ | ✓ |
### Node/Relationship Functions
Node and relationship introspection functions:
| Function | Description | kuzu_functions | QueryField |
|----------|-------------|----------------|------------|
| `id_func(node_or_rel)` | Internal ID | ✓ | ✓ |
| `label(node_or_rel)` | Label name | ✓ | ✓ |
| `labels(node_or_rel)` | Label name (alias) | ✓ | ✓ |
| `offset(node_or_rel)` | ID offset | ✓ | ✓ |
### Recursive Relationship Functions
Recursive path and traversal functions:
| Function | Description | kuzu_functions | QueryField |
|----------|-------------|----------------|------------|
| `nodes(path)` | Get nodes from path | ✓ | ✓ |
| `rels(path)` | Get relationships from path | ✓ | ✓ |
| `properties(path, property)` | Get property from collection | ✓ | ✓ |
| `is_trail(path)` | Path is trail (repeated rels) | ✓ | ✓ |
| `is_acyclic(path)` | Path is acyclic (no repeated nodes) | ✓ | ✓ |
| `length(path)` | Path length (number of rels) | ✓ | ✓ |
| `cost(path)` | Weighted path cost | ✓ | ✓ |
### Array Functions
Array-specific mathematical and similarity functions:
| Function | Description | kuzu_functions | QueryField |
|----------|-------------|----------------|------------|
| `array_value(...)` | Construct array | ✓ | — |
| `array_distance(array1, array2)` | Euclidean distance | ✓ | ✓ |
| `array_squared_distance(array1, array2)` | Squared distance | ✓ | ✓ |
| `array_dot_product(array1, array2)` | Dot product | ✓ | ✓ |
| `array_inner_product(array1, array2)` | Inner product | ✓ | ✓ |
| `array_cross_product(array1, array2)` | Cross product | ✓ | ✓ |
| `array_cosine_similarity(array1, array2)` | Cosine similarity | ✓ | ✓ |
### Blob Functions
Binary data manipulation functions:
| Function | Description | kuzu_functions | QueryField |
|----------|-------------|----------------|------------|
| `blob(data)` | Create blob | ✓ | ✓ |
| `encode(data)` | Encode string to blob | ✓ | ✓ |
| `decode(blob)` | Decode blob to string | ✓ | ✓ |
| `octet_length(blob)` | Blob byte length | ✓ | ✓ |
### Struct Functions
Struct manipulation functions:
| Function | Description | kuzu_functions | QueryField |
|----------|-------------|----------------|------------|
| `struct_extract(struct, field)` | Extract struct field | ✓ | ✓ |
| `to_int32(value)` | Cast to int32 | `ka.to_int32(field)` |
| `to_int16(value)` | Cast to int16 | `ka.to_int16(field)` |
| `to_float(value)` | Cast to float | `ka.to_float(field)` |
| `to_date(value)` | Cast to date | `ka.to_date(field)` |
| `to_timestamp(value)` | Cast to timestamp | `ka.to_timestamp(field)` |
| `cast_as(value, type)` | Cast using AS syntax | `ka.cast_as(field, "INT64")` |
| `case([input])` | CASE expression | `ka.case()` |
### Hash Functions
Cryptographic hash functions:
| Function | Description | kuzu_functions | QueryField |
|----------|-------------|----------------|------------|
| `md5(data)` | MD5 hash | ✓ | ✓ |
| `sha256(data)` | SHA256 hash | ✓ | ✓ |
| `hash(data)` | Generic hash | ✓ | ✓ |
### UUID Functions
UUID generation and manipulation functions:
| Function | Description | kuzu_functions | QueryField |
|----------|-------------|----------------|------------|
| `gen_random_uuid()` | Generate random UUID | ✓ | — |
| `uuid(string)` | Parse UUID string | ✓ | ✓ |
### Utility Functions
Utility and miscellaneous functions:
| Function | Description | kuzu_functions | QueryField |
|----------|-------------|----------------|------------|
| `coalesce(val1, val2, ...)` | First non-NULL value | ✓ | ✓ |
| `ifnull(value, replacement)` | Replace NULL with value | ✓ | ✓ |
| `nullif(a, b)` | NULL if equal | ✓ | ✓ |
| `typeof(value)` | Get value type | ✓ | ✓ |
| `constant_or_null(constant, check)` | Constant if check non-NULL | ✓ | ✓ |
| `count_if(condition)` | 1 if condition true else 0 | ✓ | ✓ |
| `error(message)` | Raise runtime error | ✓ | ✓ |
### Casting Functions
Type conversion and casting functions:
|----------|-------------|----------------|------------|
| `to_int64(value)` | Cast to INT64 | ✓ | — |
| `to_int32(value)` | Cast to INT32 | ✓ | — |
| `to_int16(value)` | Cast to INT16 | ✓ | — |
| `to_double(value)` | Cast to DOUBLE | ✓ | — |
| `to_float(value)` | Cast to FLOAT | ✓ | — |
| `to_string(value)` | Cast to STRING | ✓ | — |
| `to_date(value)` | Cast to DATE | ✓ | — |
| `to_timestamp(value)` | Cast to TIMESTAMP | ✓ | ✓ |
| `cast(value, type)` | CAST function | ✓ | ✓ |
| `cast_as(value, type)` | CAST AS syntax | ✓ | ✓ |
| `case([input])` | Create CASE expression | ✓ | ✓ |
## Operator Reference
KuzuAlchemy operator support (exactly as implemented):
### Comparison Operators
| Operator/Method | Description | Backing Enum/Method |
|-----------------|-------------|---------------------|
| `==` | Equal to | QueryField.__eq__ -> ComparisonOperator.EQ |
| `!=` | Not equal | QueryField.__ne__ -> ComparisonOperator.NEQ |
| `<` | Less than | QueryField.__lt__ -> ComparisonOperator.LT |
| `<=` | Less than or equal | QueryField.__le__ -> ComparisonOperator.LTE |
| `>` | Greater than | QueryField.__gt__ -> ComparisonOperator.GT |
| `>=` | Greater than or equal | QueryField.__ge__ -> ComparisonOperator.GTE |
| `in_(values)` | Membership | QueryField.in_ -> ComparisonOperator.IN |
| `not_in(values)` | Not in | QueryField.not_in -> ComparisonOperator.NOT_IN |
| `between(a,b, inclusive=True)` | Range test | QueryField.between -> BetweenExpression |
### Pattern/Regex Operators
| Operator/Method | Description | Backing Enum |
|-----------------|-------------|--------------|
| `like(pattern, case_sensitive=True)` | Regex-like match | ComparisonOperator.LIKE |
| `not_like(pattern, case_sensitive=True)` | Negative match | ComparisonOperator.NOT_LIKE |
| `regex_match(pattern)` | `=~` regex match | ComparisonOperator.REGEX_MATCH |
| `not_regex_match(pattern)` | `!~` negative regex | ComparisonOperator.NOT_REGEX_MATCH |
### Contains/Prefix/Suffix Filters
| Method | Description | Backing Enum |
|--------|-------------|--------------|
| `contains_filter(value)` | Contains element/substr | ComparisonOperator.CONTAINS |
| `starts_with_filter(value, case_sensitive=True)` | Prefix match | ComparisonOperator.STARTS_WITH |
| `ends_with_filter(value, case_sensitive=True)` | Suffix match | ComparisonOperator.ENDS_WITH |
| `is_null()` | Field is NULL | ComparisonOperator.IS_NULL |
| `is_not_null()` | Field is NOT NULL | ComparisonOperator.IS_NOT_NULL |
### Logical Operators (on FilterExpression)
| Operator | Description | Backing |
|----------|-------------|---------|
| `&` | Logical AND | FilterExpression.__and__ -> LogicalOperator.AND |
| `|` | Logical OR | FilterExpression.__or__ -> LogicalOperator.OR |
| `^` | Logical XOR | FilterExpression.__xor__ -> LogicalOperator.XOR |
| `~` | Logical NOT | FilterExpression.__invert__ -> NotFilterExpression |
### Arithmetic Operators (on QueryField)
| Operator | Description | Backing |
|----------|-------------|---------|
| `+` | Addition / list concatenation | QueryField.__add__/__radd__ -> ArithmeticOperator.ADD |
| `-` | Subtraction | QueryField.__sub__/__rsub__ -> ArithmeticOperator.SUB |
| `*` | Multiplication | QueryField.__mul__/__rmul__ -> ArithmeticOperator.MUL |
| `/` | Division | QueryField.__truediv__/__rtruediv__ -> ArithmeticOperator.DIV |
| `%` | Modulo | QueryField.__mod__/__rmod__ -> ArithmeticOperator.MOD |
| `^` | Power | QueryField.__pow__/__rpow__ -> ArithmeticOperator.POW |
### Indexing/Slicing (on QueryField)
| Operator | Description | Backing Function |
|----------|-------------|------------------|
| `field[idx]` | 1-based index extract | FunctionExpression("array_extract") |
| `field[a:b]` | 1-based slice | FunctionExpression("array_slice") |
---
## Model Definition
### Node Models
```python
from kuzualchemy import kuzu_node, KuzuBaseModel, kuzu_field, KuzuDataType
from typing import Optional, List
from datetime import datetime
@kuzu_node("User") # Table name in Kuzu
class User(KuzuBaseModel):
# Primary key
id: int = kuzu_field(kuzu_type=KuzuDataType.INT64, primary_key=True)
# Basic fields
name: str = kuzu_field(kuzu_type=KuzuDataType.STRING, not_null=True)
email: Optional[str] = kuzu_field(kuzu_type=KuzuDataType.STRING, unique=True, default=None)
age: int = kuzu_field(kuzu_type=KuzuDataType.INT32, default=0)
# Boolean fields
is_active: bool = kuzu_field(kuzu_type=KuzuDataType.BOOL, default=True)
# Timestamp fields
created_at: datetime = kuzu_field(
kuzu_type=KuzuDataType.TIMESTAMP,
default=KuzuDefaultFunction.CURRENT_TIMESTAMP
)
# Array fields
tags: Optional[List[str]] = kuzu_field(
kuzu_type=ArrayTypeSpecification(element_type=KuzuDataType.STRING),
default=None
)
```
### Relationship Models
```python
from kuzualchemy import kuzu_relationship, KuzuRelationshipBase
@kuzu_relationship("KNOWS", pairs=[(User, User)])
class Knows(KuzuRelationshipBase):
since: datetime = kuzu_field(kuzu_type=KuzuDataType.TIMESTAMP)
strength: float = kuzu_field(kuzu_type=KuzuDataType.DOUBLE, default=1.0)
```
### Model Methods
Every model inherits these methods from `KuzuBaseModel`:
```python
class User(KuzuBaseModel):
# Built-in methods available:
def save(self, session: KuzuSession) -> None:
"""Save instance to database"""
pass
def delete(self, session: KuzuSession) -> None:
"""Delete instance from database"""
pass
@classmethod
def query(cls, session: KuzuSession = None) -> Query:
"""Create query for this model"""
pass
@classmethod
def get_primary_key_fields(cls) -> List[str]:
"""Get primary key field names"""
pass
@classmethod
def get_foreign_key_fields(cls) -> Dict[str, ForeignKeyMetadata]:
"""Get foreign key fields"""
pass
```
---
## Field Types & Metadata
### Supported Kuzu Data Types
```python
from kuzualchemy import KuzuDataType
# Numeric types
KuzuDataType.INT8, KuzuDataType.INT16, KuzuDataType.INT32, KuzuDataType.INT64
KuzuDataType.UINT8, KuzuDataType.UINT16, KuzuDataType.UINT32, KuzuDataType.UINT64
KuzuDataType.FLOAT, KuzuDataType.DOUBLE
KuzuDataType.DECIMAL, KuzuDataType.SERIAL
# String types
KuzuDataType.STRING, KuzuDataType.BLOB
# Boolean type
KuzuDataType.BOOL
# Date/time types
KuzuDataType.DATE, KuzuDataType.TIMESTAMP, KuzuDataType.INTERVAL
# UUID type
KuzuDataType.UUID
# Complex types
KuzuDataType.STRUCT, KuzuDataType.MAP, KuzuDataType.UNION
```
### Field Definition
```python
# Field definition
field = kuzu_field(
# Basic properties
kuzu_type=KuzuDataType.STRING,
primary_key=False,
unique=False,
not_null=False,
index=False,
# Default values
default="default_value",
default_factory=lambda: "computed_default",
# Constraints
check_constraint="LENGTH(field_name) > 0",
# Foreign keys
foreign_key=ForeignKeyMetadata(
target_model=TargetModel,
target_field="id",
on_delete=CascadeAction.CASCADE,
on_update=CascadeAction.SET_NULL
),
# Metadata
alias="field_alias",
title="Field Title",
description="Field description"
)
```
### Array Fields
```python
from kuzualchemy.kuzu_orm import ArrayTypeSpecification
class User(KuzuBaseModel):
# Array field definition
tags: List[str] = kuzu_field(
kuzu_type=ArrayTypeSpecification(element_type=KuzuDataType.STRING),
default=None
)
```
### Default Values
```python
from kuzualchemy.constants import KuzuDefaultFunction
class User(KuzuBaseModel):
# Static defaults
status: str = kuzu_field(kuzu_type=KuzuDataType.STRING, default="active")
# Function defaults
created_at: datetime = kuzu_field(
kuzu_type=KuzuDataType.TIMESTAMP,
default=KuzuDefaultFunction.CURRENT_TIMESTAMP
)
# Factory defaults
uuid_field: str = kuzu_field(
kuzu_type=KuzuDataType.UUID,
default_factory=lambda: str(uuid.uuid4())
)
```
---
## Relationships
### Basic Relationships
```python
from kuzualchemy import kuzu_relationship, KuzuRelationshipBase
@kuzu_relationship("FOLLOWS", pairs=[(User, User)])
class Follows(KuzuRelationshipBase):
since: datetime = kuzu_field(kuzu_type=KuzuDataType.TIMESTAMP)
weight: float = kuzu_field(kuzu_type=KuzuDataType.DOUBLE, default=1.0)
```
### Multi-Pair Relationships
```python
# Multiple relationship pairs
@kuzu_relationship("AUTHORED", pairs=[
(User, {Post, Comment}),
(Organization, Post)
])
class Authored(KuzuRelationshipBase):
created_at: datetime = kuzu_field(kuzu_type=KuzuDataType.TIMESTAMP)
role: str = kuzu_field(kuzu_type=KuzuDataType.STRING, default="author")
```
### Relationship Usage
```python
# Create relationships
user1 = User(id=1, name="Alice")
user2 = User(id=2, name="Bob")
follows = Follows(from_node=user1, to_node=user2, since=datetime.now())
session.add_all([user1, user2, follows])
session.commit()
```
---
## Query System
### Basic Queries
```python
from kuzualchemy import Query
# Create query
query = Query(User, session=session)
# Simple filtering
filtered = query.where(query.fields.age > 25)
results = filtered.all()
# Method chaining
results = (Query(User, session=session)
.where(Query(User, session=session).fields.name.starts_with("A"))
.where(Query(User, session=session).fields.age.between(20, 40))
.order_by(Query(User, session=session).fields.name.asc())
.limit(10)
.all())
```
### Advanced Queries
```python
# Aggregation with HAVING: count users by age > 1
q = Query(User, session=session)
agg = q.count() # COUNT(*) AS count
count_by_age = (
q.group_by(q.fields.age)
.having(ka.to_int64("count") > 1) # compare aggregated alias post-WITH using cast
)
# Relationship join (pattern: join(TargetModel, RelationshipClass, ...))
q = Query(User, session=session)
joined = q.join(User, Follows, target_alias="u2")
# Subquery: authors older than 30
subq = Query(User, session=session).where(Query(User, session=session).fields.age > 30)
main_query = Query(Post, session=session).where(
Query(Post, session=session).fields.author_id.in_(subq.select("id"))
)
```
### Function Usage in Queries
```python
import kuzualchemy as ka
# Text functions
query = Query(User, session=session)
text_query = query.where(
ka.upper(query.fields.name).starts_with("A")
)
# Numeric functions
numeric_query = query.where(
ka.abs(query.fields.age - 30) < 5
)
# List functions
list_query = query.where(
ka.list_contains(query.fields.hobbies, "reading")
)
# Date functions
date_query = query.where(
ka.date_part("year", query.fields.birth_date) > 1990
)
```
### Query Results
```python
# Get all results
results = query.all() # List[ModelType]
# Get first result
first = query.first() # ModelType | None
# Get exactly one result
one = query.one() # ModelType (raises if 0 or >1)
# Get one or none
one_or_none = query.one_or_none() # ModelType | None
# Check existence
exists = query.exists() # bool
# Count results
count_query = query.count() # Query with COUNT(*) AS count aggregation
count = count_query._execute()[0]["count"] if count_query._execute() else 0
```
---
## Session Management
### Basic Session Usage
```python
from kuzualchemy import KuzuSession
from pathlib import Path
# Create session
session = KuzuSession(db_path=Path("my_database.db"))
# Execute DDL
ddl = get_all_ddl()
if ddl.strip():
session.execute(ddl)
# Add and commit
user = User(id=1, name="Alice", email="alice@example.com")
session.add(user)
session.commit()
# Close session
session.close()
```
### Transaction Management
```python
# Manual transactions using KuzuTransaction
from kuzualchemy import KuzuTransaction
with KuzuTransaction(session):
user = User(id=1, name="Alice")
session.add(user)
# Automatic commit on success, rollback on exception
# Or using session.begin() context manager
with session.begin():
user = User(id=1, name="Alice")
session.add(user)
# Automatic commit on success, rollback on exception
```
### Session Factory
```python
from kuzualchemy import SessionFactory
# Create factory
factory = SessionFactory(
db_path="database.db",
autoflush=True,
autocommit=False
)
# Create sessions
session1 = factory.create_session()
session2 = factory.create_session(autocommit=True) # Override defaults
# Session scope context manager
with factory.session_scope() as session:
user = User(id=1, name="Alice")
session.add(user)
# Automatic commit/rollback
```
### Connection Management
```python
from kuzualchemy import KuzuConnection
# Direct connection usage
connection = KuzuConnection(db_path="database.db")
session = KuzuSession(connection=connection)
# Connection sharing
session1 = KuzuSession(connection=connection)
session2 = KuzuSession(connection=connection)
```
---
## Advanced Features
### Registry Management
```python
from kuzualchemy import (
get_registered_nodes,
get_registered_relationships,
get_all_models,
clear_registry,
validate_all_models
)
# Access registered models
nodes = get_registered_nodes()
relationships = get_registered_relationships()
all_models = get_all_models()
# Validate all models
validation_errors = validate_all_models()
# Clear registry (useful for testing)
clear_registry()
```
### Enhanced Base Model with Enum Conversion
```python
from kuzualchemy import BaseModel
from enum import Enum
class Status(Enum):
ACTIVE = "active"
INACTIVE = "inactive"
@kuzu_node("Account")
class Account(BaseModel): # Automatic enum conversion
status: Status = kuzu_field(kuzu_type=KuzuDataType.STRING)
# BaseModel automatically converts enums to/from string values
```
### Foreign Key Support
```python
from kuzualchemy import ForeignKeyMetadata, CascadeAction
@kuzu_node("Post")
class Post(KuzuBaseModel):
id: int = kuzu_field(kuzu_type=KuzuDataType.INT64, primary_key=True)
title: str = kuzu_field(kuzu_type=KuzuDataType.STRING)
author_id: int = kuzu_field(
kuzu_type=KuzuDataType.INT64,
foreign_key=ForeignKeyMetadata(
target_model=User,
target_field="id",
on_delete=CascadeAction.CASCADE
)
)
```
### Custom Functions
```python
# All Kuzu functions are available as standalone callables
import kuzualchemy as ka
# Use in queries
query = Query(User, session=session).where(
ka.concat(query.fields.first_name, " ", query.fields.last_name).contains("Alice")
)
# Use in expressions
full_name = ka.concat(user.first_name, " ", user.last_name)
```
### Complex Expressions
```python
# Combine multiple functions and operators
complex_filter = (
ka.upper(query.fields.name).starts_with("A") &
(query.fields.age.between(20, 40)) &
ka.list_contains(query.fields.tags, "python")
)
results = Query(User, session=session).where(complex_filter).all()
```
---
## API Reference
### Core Classes
#### KuzuBaseModel
Base class for all node models with built-in ORM functionality.
**Methods:**
- `save(session: KuzuSession) -> None`: Save instance to database
- `delete(session: KuzuSession) -> None`: Delete instance from database
- `query(session: KuzuSession = None) -> Query`: Create query for this model
- `get_kuzu_metadata(field_name: str) -> KuzuFieldMetadata`: Get field metadata
- `get_all_kuzu_metadata() -> Dict[str, KuzuFieldMetadata]`: Get all field metadata
- `get_primary_key_fields() -> List[str]`: Get primary key field names
- `get_foreign_key_fields() -> Dict[str, ForeignKeyMetadata]`: Get foreign key fields
#### KuzuRelationshipBase
Base class for relationship models.
**Methods:**
- Same as KuzuBaseModel plus relationship-specific functionality
- `create_between(from_node, to_node, **properties) -> KuzuRelationshipBase`: Factory to instantiate relationship between nodes
- `from_node_pk`/`to_node_pk` properties for node primary keys
#### KuzuSession
Main session class for database operations.
**Methods:**
- `execute(query: str, parameters: Dict = None) -> List[Dict]`: Execute raw query
- `add(instance: Any) -> None`: Add instance to session
- `add_all(instances: List[Any]) -> None`: Add multiple instances
- `delete(instance: Any) -> None`: Mark instance for deletion
- `commit() -> None`: Commit current transaction
- `rollback() -> None`: Rollback current transaction
- `flush() -> None`: Flush pending changes
- `close() -> None`: Close session
- `begin() -> KuzuTransaction`: Begin transaction context
#### Query[ModelType]
Type-safe query builder.
**Methods:**
- `where(expression: FilterExpression) -> Query`: Add WHERE clause
- `filter(*expressions: FilterExpression) -> Query`: Add multiple filters
- `order_by(*fields: QueryField) -> Query`: Add ORDER BY clause
- `group_by(*fields: QueryField) -> Query`: Add GROUP BY clause
- `having(expression: FilterExpression) -> Query`: Add HAVING clause
- `limit(count: int) -> Query`: Add LIMIT clause
- `offset(count: int) -> Query`: Add OFFSET clause
- `distinct() -> Query`: Add DISTINCT clause
- `all() -> List[ModelType]`: Execute and return all results
- `first() -> ModelType | None`: Execute and return first result
- `one() -> ModelType`: Execute and return exactly one result
- `one_or_none() -> ModelType | None`: Execute and return one or none
- `count() -> int`: Count results
- `exists() -> bool`: Check if results exist
### Field Definition
#### kuzu_field Function
Field definition with options:
```python
kuzu_field(
default: Any = ..., # Default value
kuzu_type: Union[KuzuDataType, str, ArrayTypeSpecification], # Kuzu data type
primary_key: bool = False, # Primary key flag
foreign_key: ForeignKeyMetadata = None, # Foreign key metadata
unique: bool = False, # Unique constraint
not_null: bool = False, # NOT NULL constraint
index: bool = False, # Index flag
check_constraint: str = None, # CHECK constraint
default_factory: Callable[[], Any] = None, # Default factory function
alias: str = None, # Field alias
title: str = None, # Field title
description: str = None, # Field description
)
```
### Decorators
#### @kuzu_node()
Mark class as Kuzu node:
```python
@kuzu_node(
name: str = None, # Node name (defaults to class name)
abstract: bool = False, # Abstract node flag
compound_indexes: List[CompoundIndex] = None, # Compound indexes
table_constraints: List[str] = None, # Table constraints
properties: Dict[str, Any] = None # Additional properties
)
```
#### @kuzu_relationship()
Mark class as Kuzu relationship:
```python
@kuzu_relationship(
name: str = None, # Relationship name
pairs: List[Tuple[Type, Type]] = None, # Valid node pairs
multiplicity: RelationshipMultiplicity = MANY_TO_MANY, # Relationship multiplicity
compound_indexes: List[CompoundIndex] = None, # Compound indexes
table_constraints: List[str] = None, # Table constraints
properties: Dict[str, Any] = None # Additional properties
)
```
### Enums and Constants
#### KuzuDataType
All supported Kuzu data types:
- Numeric: `INT8`, `INT16`, `INT32`, `INT64`, `UINT8`, `UINT16`, `UINT32`, `UINT64`, `FLOAT`, `DOUBLE`, `DECIMAL`, `SERIAL`
- String: `STRING`, `BLOB`
- Boolean: `BOOL`
- Temporal: `DATE`, `TIMESTAMP`, `INTERVAL`
- Other: `UUID`, `STRUCT`, `MAP`, `UNION`
#### ComparisonOperator
Query comparison operators (from kuzu_query_expressions):
- `EQ`, `NEQ`: Equality/inequality
- `LT`, `LTE`, `GT`, `GTE`: Comparison operators
- `IN`, `NOT_IN`: List membership
- `LIKE`, `NOT_LIKE`: Pattern matching
- `IS_NULL`, `IS_NOT_NULL`: Null checks
- `CONTAINS`: String/array contains
- `STARTS_WITH`, `ENDS_WITH`: String prefix/suffix
- `EXISTS`, `NOT_EXISTS`: Existence checks
#### LogicalOperator
Logical operators for combining conditions:
- `AND`, `OR`, `NOT`, `XOR`: Boolean logic
#### AggregateFunction
Aggregate functions:
- `COUNT`, `COUNT_DISTINCT`: Counting
- `SUM`, `AVG`: Numeric aggregation
- `MIN`, `MAX`: Extrema
- `COLLECT`, `COLLECT_LIST`, `COLLECT_SET`: Collection aggregation
#### OrderDirection
Ordering directions:
- `ASC`: Ascending order
- `DESC`: Descending order
#### JoinType
Join types:
- `INNER`: Inner join
- `OPTIONAL`: Optional match (left outer join)
### Utility Functions
#### DDL Generation
- `get_all_ddl() -> str`: Generate DDL for all registered models
- `get_ddl_for_node(node_cls: Type[Any]) -> str`: Generate DDL for specific node
- `get_ddl_for_relationship(rel_cls: Type[Any]) -> str`: Generate DDL for specific relationship
#### Registry Management
- `get_registered_nodes() -> Dict[str, Type[Any]]`: Get all registered nodes
- `get_registered_relationships() -> Dict[str, Type[Any]]`: Get all registered relationships
- `get_all_models() -> Dict[str, Type[Any]]`: Get all registered models
- `clear_registry() -> None`: Clear model registry
- `validate_all_models() -> List[str]`: Validate all registered models
#### Test Utilities
- `initialize_schema(session: KuzuSession, ddl: str = None) -> None`: Initialize database schema
---
---
## Contributing
We welcome contributions to KuzuAlchemy! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
### Development Setup
```bash
# Clone repository
git clone <repository-url>
cd kuzualchemy
# Install in development mode
pip install -e ".[dev,test]"
# Run tests
pytest
# Run type checking
mypy src/
# Run linting
flake8 src/
black src/
# Build package
python -m build
```
### Testing
```bash
# Run all tests
pytest
# Run specific test categories
pytest tests/test_functions.py
pytest tests/test_integration.py
# Run with coverage
pytest --cov=kuzualchemy --cov-report=html
```
---
## License
This project is licensed under the GPL-3.0 license - see the [LICENSE](LICENSE) file for details.
---
## Conclusion
KuzuAlchemy is an Object-Relational Mapping library for the Kuzu graph database. It provides a SQLAlchemy-like interface for working with graph data.
Raw data
{
"_id": null,
"home_page": null,
"name": "kuzualchemy",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": null,
"keywords": "kuzu, graph, database, orm, sqlalchemy, cypher, graph-database",
"author": null,
"author_email": "FanaticPythoner <info@kuzualchemy.com>",
"download_url": "https://files.pythonhosted.org/packages/1d/db/e93e2f5c8185dde067626d26c0946081f5989141069e7346055284fa8e2e/kuzualchemy-0.2.0.tar.gz",
"platform": null,
"description": "# KuzuAlchemy\n\nA SQLAlchemy-like ORM for Kuzu graph database\n\n<!-- KUZUALCHEMY-AUTO-UPDATE-START -->\n# Version: 0.2.0\n**Status**: Alpha\n**Tests**: \u23f3 Running tests... (Last updated: 2025-09-04 18:35:11 UTC)\n\n[](https://github.com/FanaticPythoner/kuzualchemy/actions/workflows/test.yml)\n[](https://badge.fury.io/py/kuzualchemy)\n[](https://pypi.org/project/kuzualchemy/)\n\nKuzuAlchemy is an Object-Relational Mapping (ORM) library for the [Kuzu graph database](https://kuzudb.com/). It provides a SQLAlchemy-like interface for working with graph data.\n\n> **Note**: This software is currently in alpha development. APIs may change.\n<!-- KUZUALCHEMY-AUTO-UPDATE-END -->\n\n## Table of Contents\n\n1. [Overview](#overview)\n2. [Installation](#installation)\n3. [Quick Start](#quick-start)\n4. [Function Reference](#function-reference)\n5. [Operator Reference](#operator-reference)\n6. [Model Definition](#model-definition)\n7. [Field Types & Metadata](#field-types--metadata)\n8. [Relationships](#relationships)\n9. [Query System](#query-system)\n10. [Session Management](#session-management)\n11. [Advanced Features](#advanced-features)\n12. [API Reference](#api-reference)\n13. [Contributing](#contributing)\n14. [License](#license)\n\n## Overview\n\nKuzuAlchemy provides the following components:\n\n- **Core ORM** (`kuzu_orm.py`): Base classes for nodes and relationships with metadata handling\n- **Session Management** (`kuzu_session.py`): Database operations with transaction support\n- **Query System** (`kuzu_query.py`): Query builder with Cypher generation\n- **Expression Engine** (`kuzu_query_expressions.py`): Expression system supporting Kuzu operators\n- **Function Library** (`kuzu_functions.py`): Kuzu functions implemented as standalone callables\n- **Field Integration** (`kuzu_query_fields.py`): QueryField methods providing fluent API access to functions\n\n### Key Features\n\n- **Kuzu Function Support**: Kuzu functions and operators implemented\n- **ORM**: Model definition, session management, and querying capabilities\n- **Type-Safe Operations**: Type safety with parameter handling and validation\n- **Testing**: Test coverage for functionality\n- **Error Handling**: Error handling and transaction management\n\n## Installation\n\n### Prerequisites\n\n```bash\npip install kuzu pydantic\n```\n\n### Install KuzuAlchemy\n\n```bash\npip install kuzualchemy\n```\n\n### Development Installation\n\n```bash\ngit clone <repository-url>\ncd kuzualchemy\npip install -e \".[dev,test]\"\n```\n\n## Quick Start\n\n### Basic Setup\n\n```python\nfrom kuzualchemy import (\n KuzuBaseModel, KuzuRelationshipBase,\n kuzu_node, kuzu_relationship, kuzu_field,\n KuzuDataType, KuzuSession,\n get_all_ddl\n)\n\n# Create session\nsession = KuzuSession(db_path=\"database.db\")\n\n# Initialize schema\nddl = get_all_ddl()\nif ddl.strip():\n session.execute(ddl)\n```\n\n### Example\n\n```python\nimport kuzualchemy as ka\nfrom pathlib import Path\n\n# Define your graph models\n@ka.kuzu_node(\"Person\")\nclass Person(ka.KuzuBaseModel):\n name: str = ka.kuzu_field(kuzu_type=ka.KuzuDataType.STRING, primary_key=True)\n age: int = ka.kuzu_field(kuzu_type=ka.KuzuDataType.INT32)\n email: str = ka.kuzu_field(kuzu_type=ka.KuzuDataType.STRING)\n\n@ka.kuzu_relationship(\"KNOWS\", pairs=[(Person, Person)])\nclass Knows(ka.KuzuRelationshipBase):\n since: int = ka.kuzu_field(kuzu_type=ka.KuzuDataType.INT32)\n strength: float = ka.kuzu_field(kuzu_type=ka.KuzuDataType.DOUBLE, default=1.0)\n\n# Create database and session\ndb_path = Path(\"my_graph.db\")\nsession = ka.KuzuSession(db_path)\n\n# Create schema\nsession.execute(ka.get_all_ddl())\n\n# Insert data\nalice = Person(name=\"Alice\", age=30, email=\"alice@example.com\")\nbob = Person(name=\"Bob\", age=25, email=\"bob@example.com\")\nknows = Knows(from_node=alice, to_node=bob, since=2020, strength=0.9)\n\n# Or, you could do `session.add_all([alice, bob, knows])`\nsession.add(alice)\nsession.add(bob)\nsession.add(knows)\nsession.commit()\n\n# Query data\nquery = ka.Query(Person, session=session)\nfiltered_query = query.where(query.fields.age > 25)\nresults = filtered_query.all()\n\nprint(f\"Found {len(results)} people over 25\")\n```\n\n---\n\n## Function Reference\n\nKuzuAlchemy implements Kuzu functions across multiple categories. Each function returns a `FunctionExpression` object that can be used in queries and expressions.\n\n### Text Functions\n\nString manipulation and text processing functions:\n\n| Function | Description | kuzu_functions | QueryField |\n|----------|-------------|----------------|------------|\n| `concat(*args)` | Concatenate multiple strings | \u2713 | \u2713 |\n| `ws_concat(separator, *args)` | Concatenate strings with separator | \u2713 | \u2014 |\n| `array_extract(string_or_list, index)` | Extract element at 1-based index from string or list | \u2713 | \u2014 |\n| `array_slice(string_or_list, begin, end)` | Slice string or list (1-based) | \u2713 | \u2014 |\n| `list_element(list_value, index)` | Extract list element at index | \u2713 | \u2713 |\n| `list_extract(list_value, index)` | Extract list element at index (alias) | \u2713 | \u2713 |\n| `contains(string1, string2)` | Substring test | \u2713 | \u2713 |\n| `ends_with(string1, string2)` | Ends-with test (alias of suffix) | \u2713 | \u2713 |\n| `lower(string)` | Lowercase | \u2713 | \u2713 |\n| `lcase(string)` | Lowercase (alias) | \u2713 | \u2713 |\n| `left(string, count)` | Left substring | \u2713 | \u2713 |\n| `levenshtein(s1, s2)` | Edit distance | \u2713 | \u2713 |\n| `lpad(string, count, character)` | Left pad | \u2713 | \u2713 |\n| `ltrim(string)` | Trim left | \u2713 | \u2713 |\n| `prefix(string, search_string)` | Starts-with test | \u2713 | \u2014 |\n| `repeat(string, count)` | Repeat string | \u2713 | \u2713 |\n| `reverse(string)` | Reverse string | \u2713 | \u2713 |\n| `right(string, count)` | Right substring | \u2713 | \u2713 |\n| `rpad(string, count, character)` | Right pad | \u2713 | \u2713 |\n| `rtrim(string)` | Trim right | \u2713 | \u2713 |\n| `starts_with(string1, string2)` | Starts-with test (alias of prefix) | \u2713 | \u2713 |\n| `substring(string, start, length)` | Substring by 1-based start/length | \u2713 | \u2713 |\n| `substr(string, start, length)` | Substring (alias) | \u2713 | \u2713 |\n| `suffix(string, search_string)` | Ends-with test | \u2713 | \u2014 |\n| `trim(string)` | Trim both sides | \u2713 | \u2713 |\n| `upper(string)` | Uppercase | \u2713 | \u2713 |\n| `ucase(string)` | Uppercase (alias) | \u2713 | \u2713 |\n| `initcap(string)` | Capitalize first letter | \u2713 | \u2713 |\n| `string_split(string, separator)` | Split to array | \u2713 | \u2713 |\n| `split_part(string, separator, index)` | Part at 1-based index | \u2713 | \u2713 |\n\n### Pattern Matching Functions\n\nRegular expression utilities:\n\n| Function | Description | kuzu_functions | QueryField |\n|----------|-------------|----------------|------------|\n| `regexp_matches(string, pattern)` | Regex test | \u2713 | \u2713 |\n| `regexp_replace(string, pattern, replacement[, options])` | Regex replace | \u2713 | \u2713 |\n| `regexp_extract(string, pattern[, group])` | Extract first match/group | \u2713 | \u2713 |\n| `regexp_extract_all(string, pattern[, group])` | Extract all matches/groups | \u2713 | \u2713 |\n| `regexp_split_to_array(string, pattern[, options])` | Split by regex | \u2713 | \u2713 |\n\n\n### List Functions\n\nArray and list manipulation functions:\n\n| Function | Description | kuzu_functions | QueryField |\n|----------|-------------|----------------|------------|\n| `list_creation(...)` | Create a list containing the argument values | \u2713 | \u2014 |\n| `size(value)` | Return size of string or list | \u2713 | \u2713 |\n| `list_concat(list1, list2)` | Concatenate two lists | \u2713 | \u2713 |\n| `range(start, stop, [step])` | Return list from start to stop with step | \u2713 | \u2014 |\n| `list_cat(list1, list2)` | Alias of list_concat | \u2713 | \u2713 |\n| `array_concat(list1, list2)` | Alias of list_concat | \u2713 | \u2713 |\n| `array_cat(list1, list2)` | Alias of list_concat | \u2713 | \u2713 |\n| `list_append(list, element)` | Append element to list | \u2713 | \u2713 |\n| `array_append(list, element)` | Alias of list_append | \u2713 | \u2713 |\n| `array_push_back(list, element)` | Alias of list_append | \u2713 | \u2713 |\n| `list_prepend(list, element)` | Prepend element to list | \u2713 | \u2713 |\n| `array_prepend(list, element)` | Alias of list_prepend | \u2713 | \u2713 |\n| `array_push_front(list, element)` | Alias of list_prepend | \u2713 | \u2713 |\n| `list_position(list, element)` | Position of element in list | \u2713 | \u2713 |\n| `list_indexof(list, element)` | Alias of list_position | \u2713 | \u2713 |\n| `array_position(list, element)` | Alias of list_position | \u2713 | \u2713 |\n| `array_indexof(list, element)` | Alias of list_position | \u2713 | \u2713 |\n| `list_contains(list, element)` | Check if list contains element | \u2713 | \u2713 |\n| `list_has(list, element)` | Alias of list_contains | \u2713 | \u2713 |\n| `array_contains(list, element)` | Alias of list_contains | \u2713 | \u2713 |\n| `array_has(list, element)` | Alias of list_contains | \u2713 | \u2713 |\n| `list_slice(list, begin, end)` | Extract sub-list | \u2713 | \u2713 |\n\n### Advanced List Functions\n\nHigher-order and quantifier list functions (order matches kuzu_functions.py):\n\n| Function | Description | kuzu_functions | QueryField |\n|----------|-------------|----------------|------------|\n| `list_reverse(list)` | Reverse list elements | \u2713 | \u2713 |\n| `list_sort(list[, order, nulls])` | Sort elements of list | \u2713 | \u2713 |\n| `list_reverse_sort(list)` | Sort elements of list in DESC | \u2713 | \u2713 |\n| `list_sum(list)` | Sum elements | \u2713 | \u2713 |\n| `list_product(list)` | Multiply elements | \u2713 | \u2713 |\n| `list_distinct(list)` | Remove NULLs and duplicates | \u2713 | \u2713 |\n| `list_unique(list)` | Count unique elements | \u2713 | \u2713 |\n| `list_any_value(list)` | First non-NULL value | \u2713 | \u2713 |\n| `list_to_string(sep, list)` | Join elements with separator | \u2713 | \u2713 |\n| `list_transform(list, lambda)` | Transform elements using lambda expression | \u2713 | \u2713 |\n| `list_filter(list, lambda)` | Filter elements using lambda expression | \u2713 | \u2713 |\n| `list_reduce(list, lambda)` | Reduce list using lambda expression | \u2713 | \u2713 |\n| `list_has_all(list, sub_list)` | Contains all elements from sub-list | \u2713 | \u2713 |\n| `all_func(var, list, predicate)` | All elements satisfy predicate | \u2713 | \u2713 |\n| `any_func(var, list, predicate)` | Any element satisfies predicate | \u2713 | \u2713 |\n| `none_func(var, list, predicate)` | No elements satisfy predicate | \u2713 | \u2713 |\n| `single_func(var, list, predicate)` | Exactly one element satisfies predicate | \u2713 | \u2713 |\n\n| `array_slice(array, start, end)` | Slice array | `ka.array_slice(field, 1, 5)` |\n| `list_reverse(list)` | Reverse list | `ka.list_reverse(field)` |\n| `list_sort(list)` | Sort list | `ka.list_sort(field)` |\n| `list_reverse_sort(list)` | Reverse sort | `ka.list_reverse_sort(field)` |\n| `list_sum(list)` | Sum elements | `ka.list_sum(field)` |\n| `list_product(list)` | Product elements | `ka.list_product(field)` |\n| `list_distinct(list)` | Distinct elements | `ka.list_distinct(field)` |\n| `list_unique(list)` | Unique elements | `ka.list_unique(field)` |\n| `list_any_value(list)` | Any element | `ka.list_any_value(field)` |\n| `list_to_string(sep, list)` | Join elements | `ka.list_to_string(\",\", field)` |\n| `list_extract(list, index)` | Extract element | `ka.list_extract(field, 1)` |\n| `list_element(list, index)` | Get element | `ka.list_element(field, 1)` |\n| `range(start, end, [step])` | Generate range | `ka.range(1, 10)` |\n\n### Numeric Functions\n\nMathematical and numeric computation functions:\n\n| Function | Description | kuzu_functions | QueryField |\n|----------|-------------|----------------|------------|\n| `pi()` | Return value of pi | \u2713 | \u2014 |\n| `abs(value)` | Absolute value | \u2713 | \u2713 |\n| `ceil(value)` | Ceiling | \u2713 | \u2713 |\n| `ceiling(value)` | Ceiling (alias) | \u2713 | \u2713 |\n| `floor(value)` | Floor | \u2713 | \u2713 |\n| `round(value, precision=0)` | Round to precision | \u2713 | \u2713 |\n| `sqrt(value)` | Square root | \u2713 | \u2713 |\n| `pow(base, exponent)` | Power | \u2713 | \u2713 |\n| `sin(value)` | Sine | \u2713 | \u2713 |\n| `cos(value)` | Cosine | \u2713 | \u2713 |\n| `tan(value)` | Tangent | \u2713 | \u2713 |\n| `asin(value)` | Arcsine | \u2713 | \u2713 |\n| `acos(value)` | Arccosine | \u2713 | \u2713 |\n| `atan(value)` | Arctangent | \u2713 | \u2713 |\n| `atan2(x, y)` | Arctangent of x,y | \u2713 | \u2713 |\n| `ln(value)` | Natural log | \u2713 | \u2713 |\n| `log(value)` | Logarithm | \u2713 | \u2713 |\n| `log2(value)` | Base-2 logarithm | \u2713 | \u2713 |\n| `log10(value)` | Base-10 logarithm | \u2713 | \u2713 |\n| `negate(value)` | Negation | \u2713 | \u2713 |\n| `sign(value)` | Sign (-1,0,1) | \u2713 | \u2713 |\n| `even(value)` | Round to next even | \u2713 | \u2713 |\n| `factorial(value)` | Factorial | \u2713 | \u2713 |\n| `gamma(value)` | Gamma function | \u2713 | \u2713 |\n| `lgamma(value)` | Log Gamma | \u2713 | \u2713 |\n| `bitwise_xor(x, y)` | Bitwise XOR | \u2713 | \u2713 |\n| `cot(value)` | Cotangent | \u2713 | \u2713 |\n| `degrees(value)` | Radians to degrees | \u2713 | \u2713 |\n| `radians(value)` | Degrees to radians | \u2713 | \u2713 |\n\n### Date Functions\n\nDate manipulation and extraction functions:\n\n| Function | Description | kuzu_functions | QueryField |\n|----------|-------------|----------------|------------|\n| `current_date()` | Current date | \u2713 | \u2014 |\n| `current_timestamp()` | Current timestamp | \u2713 | \u2014 |\n| `date_part(part, date)` | Extract date part | \u2713 | \u2713 |\n| `date_trunc(part, date)` | Truncate date | \u2713 | \u2713 |\n| `datepart(part, date)` | Extract date part (alias) | \u2713 | \u2713 |\n| `datetrunc(part, date)` | Truncate date (alias) | \u2713 | \u2713 |\n| `dayname(date)` | Day name | \u2713 | \u2713 |\n| `monthname(date)` | Month name | \u2713 | \u2713 |\n| `last_day(date)` | Last day of month | \u2713 | \u2713 |\n| `greatest(...)` | Greatest of values | \u2713 | \u2713 |\n| `least(...)` | Least of values | \u2713 | \u2713 |\n| `make_date(year, month, day)` | Create date | \u2713 | \u2713 |\n\n### Timestamp Functions\n\nTimestamp manipulation and extraction functions:\n\n| Function | Description | kuzu_functions | QueryField |\n|----------|-------------|----------------|------------|\n| `century(timestamp)` | Extract century | \u2713 | \u2713 |\n| `epoch_ms(ms)` | Convert milliseconds to timestamp | \u2713 | \u2014 |\n| `to_epoch_ms(timestamp)` | Convert timestamp to milliseconds | \u2713 | \u2713 |\n\n\n### Interval Functions\n\nInterval manipulation and conversion functions:\n\n| Function | Description | kuzu_functions | QueryField |\n|----------|-------------|----------------|------------|\n| `to_years(value)` | Convert integer to year interval | \u2713 | \u2713 |\n| `to_months(value)` | Convert integer to month interval | \u2713 | \u2713 |\n| `to_days(value)` | Convert integer to day interval | \u2713 | \u2713 |\n| `to_hours(value)` | Convert integer to hour interval | \u2713 | \u2713 |\n| `to_minutes(value)` | Convert integer to minute interval | \u2713 | \u2713 |\n| `to_seconds(value)` | Convert integer to second interval | \u2713 | \u2713 |\n| `to_milliseconds(value)` | Convert integer to millisecond interval | \u2713 | \u2713 |\n| `to_microseconds(value)` | Convert integer to microsecond interval | \u2713 | \u2713 |\n\n\n### Timestamp Functions\n\nTimestamp manipulation and extraction functions (order matches kuzu_functions.py):\n\n| Function | Description | kuzu_functions | QueryField |\n|----------|-------------|----------------|------------|\n| `century(timestamp)` | Extract century | \u2713 | \u2713 |\n| `epoch_ms(ms)` | Convert milliseconds to timestamp | \u2713 | \u2014 |\n| `to_epoch_ms(timestamp)` | Convert timestamp to milliseconds | \u2713 | \u2713 |\n\n\n\n### Interval Functions\n\nInterval manipulation and conversion functions:\n\n\n### Map Functions\n\nMap manipulation and access functions:\n\n| Function | Description | kuzu_functions | QueryField |\n|----------|-------------|----------------|------------|\n| `map_func(keys, values)` | Create map from keys and values | \u2713 | \u2014 |\n| `map_extract(map, key)` | Extract value for key | \u2713 | \u2713 |\n| `element_at(map, key)` | Extract value (alias) | \u2713 | \u2713 |\n| `cardinality(map)` | Map size | \u2713 | \u2713 |\n| `map_keys(map)` | Get all keys | \u2713 | \u2713 |\n| `map_values(map)` | Get all values | \u2713 | \u2713 |\n\n### Union Functions\n\nUnion type manipulation functions:\n\n| Function | Description | kuzu_functions | QueryField |\n|----------|-------------|----------------|------------|\n| `union_value(tag := value)` | Create union with tag/value | \u2713 | \u2014 |\n| `union_tag(union)` | Get union tag | \u2713 | \u2713 |\n| `union_extract(union, tag)` | Extract value for tag | \u2713 | \u2713 |\n\n### Node/Relationship Functions\n\nNode and relationship introspection functions:\n\n| Function | Description | kuzu_functions | QueryField |\n|----------|-------------|----------------|------------|\n| `id_func(node_or_rel)` | Internal ID | \u2713 | \u2713 |\n| `label(node_or_rel)` | Label name | \u2713 | \u2713 |\n| `labels(node_or_rel)` | Label name (alias) | \u2713 | \u2713 |\n| `offset(node_or_rel)` | ID offset | \u2713 | \u2713 |\n\n### Recursive Relationship Functions\n\nRecursive path and traversal functions:\n\n| Function | Description | kuzu_functions | QueryField |\n|----------|-------------|----------------|------------|\n| `nodes(path)` | Get nodes from path | \u2713 | \u2713 |\n| `rels(path)` | Get relationships from path | \u2713 | \u2713 |\n| `properties(path, property)` | Get property from collection | \u2713 | \u2713 |\n| `is_trail(path)` | Path is trail (repeated rels) | \u2713 | \u2713 |\n| `is_acyclic(path)` | Path is acyclic (no repeated nodes) | \u2713 | \u2713 |\n| `length(path)` | Path length (number of rels) | \u2713 | \u2713 |\n| `cost(path)` | Weighted path cost | \u2713 | \u2713 |\n\n### Array Functions\n\nArray-specific mathematical and similarity functions:\n\n| Function | Description | kuzu_functions | QueryField |\n|----------|-------------|----------------|------------|\n| `array_value(...)` | Construct array | \u2713 | \u2014 |\n| `array_distance(array1, array2)` | Euclidean distance | \u2713 | \u2713 |\n| `array_squared_distance(array1, array2)` | Squared distance | \u2713 | \u2713 |\n| `array_dot_product(array1, array2)` | Dot product | \u2713 | \u2713 |\n| `array_inner_product(array1, array2)` | Inner product | \u2713 | \u2713 |\n| `array_cross_product(array1, array2)` | Cross product | \u2713 | \u2713 |\n| `array_cosine_similarity(array1, array2)` | Cosine similarity | \u2713 | \u2713 |\n\n\n### Blob Functions\n\nBinary data manipulation functions:\n\n| Function | Description | kuzu_functions | QueryField |\n|----------|-------------|----------------|------------|\n| `blob(data)` | Create blob | \u2713 | \u2713 |\n| `encode(data)` | Encode string to blob | \u2713 | \u2713 |\n| `decode(blob)` | Decode blob to string | \u2713 | \u2713 |\n| `octet_length(blob)` | Blob byte length | \u2713 | \u2713 |\n\n### Struct Functions\n\nStruct manipulation functions:\n\n| Function | Description | kuzu_functions | QueryField |\n|----------|-------------|----------------|------------|\n| `struct_extract(struct, field)` | Extract struct field | \u2713 | \u2713 |\n\n| `to_int32(value)` | Cast to int32 | `ka.to_int32(field)` |\n| `to_int16(value)` | Cast to int16 | `ka.to_int16(field)` |\n| `to_float(value)` | Cast to float | `ka.to_float(field)` |\n| `to_date(value)` | Cast to date | `ka.to_date(field)` |\n| `to_timestamp(value)` | Cast to timestamp | `ka.to_timestamp(field)` |\n| `cast_as(value, type)` | Cast using AS syntax | `ka.cast_as(field, \"INT64\")` |\n| `case([input])` | CASE expression | `ka.case()` |\n\n### Hash Functions\n\nCryptographic hash functions:\n\n| Function | Description | kuzu_functions | QueryField |\n|----------|-------------|----------------|------------|\n| `md5(data)` | MD5 hash | \u2713 | \u2713 |\n| `sha256(data)` | SHA256 hash | \u2713 | \u2713 |\n| `hash(data)` | Generic hash | \u2713 | \u2713 |\n\n### UUID Functions\n\nUUID generation and manipulation functions:\n\n| Function | Description | kuzu_functions | QueryField |\n|----------|-------------|----------------|------------|\n| `gen_random_uuid()` | Generate random UUID | \u2713 | \u2014 |\n| `uuid(string)` | Parse UUID string | \u2713 | \u2713 |\n\n### Utility Functions\n\nUtility and miscellaneous functions:\n\n| Function | Description | kuzu_functions | QueryField |\n|----------|-------------|----------------|------------|\n| `coalesce(val1, val2, ...)` | First non-NULL value | \u2713 | \u2713 |\n| `ifnull(value, replacement)` | Replace NULL with value | \u2713 | \u2713 |\n| `nullif(a, b)` | NULL if equal | \u2713 | \u2713 |\n| `typeof(value)` | Get value type | \u2713 | \u2713 |\n| `constant_or_null(constant, check)` | Constant if check non-NULL | \u2713 | \u2713 |\n| `count_if(condition)` | 1 if condition true else 0 | \u2713 | \u2713 |\n| `error(message)` | Raise runtime error | \u2713 | \u2713 |\n\n\n### Casting Functions\n\nType conversion and casting functions:\n\n|----------|-------------|----------------|------------|\n| `to_int64(value)` | Cast to INT64 | \u2713 | \u2014 |\n| `to_int32(value)` | Cast to INT32 | \u2713 | \u2014 |\n| `to_int16(value)` | Cast to INT16 | \u2713 | \u2014 |\n| `to_double(value)` | Cast to DOUBLE | \u2713 | \u2014 |\n| `to_float(value)` | Cast to FLOAT | \u2713 | \u2014 |\n| `to_string(value)` | Cast to STRING | \u2713 | \u2014 |\n| `to_date(value)` | Cast to DATE | \u2713 | \u2014 |\n| `to_timestamp(value)` | Cast to TIMESTAMP | \u2713 | \u2713 |\n| `cast(value, type)` | CAST function | \u2713 | \u2713 |\n| `cast_as(value, type)` | CAST AS syntax | \u2713 | \u2713 |\n| `case([input])` | Create CASE expression | \u2713 | \u2713 |\n\n\n\n\n## Operator Reference\n\nKuzuAlchemy operator support (exactly as implemented):\n\n### Comparison Operators\n\n| Operator/Method | Description | Backing Enum/Method |\n|-----------------|-------------|---------------------|\n| `==` | Equal to | QueryField.__eq__ -> ComparisonOperator.EQ |\n| `!=` | Not equal | QueryField.__ne__ -> ComparisonOperator.NEQ |\n| `<` | Less than | QueryField.__lt__ -> ComparisonOperator.LT |\n| `<=` | Less than or equal | QueryField.__le__ -> ComparisonOperator.LTE |\n| `>` | Greater than | QueryField.__gt__ -> ComparisonOperator.GT |\n| `>=` | Greater than or equal | QueryField.__ge__ -> ComparisonOperator.GTE |\n| `in_(values)` | Membership | QueryField.in_ -> ComparisonOperator.IN |\n| `not_in(values)` | Not in | QueryField.not_in -> ComparisonOperator.NOT_IN |\n| `between(a,b, inclusive=True)` | Range test | QueryField.between -> BetweenExpression |\n\n### Pattern/Regex Operators\n\n| Operator/Method | Description | Backing Enum |\n|-----------------|-------------|--------------|\n| `like(pattern, case_sensitive=True)` | Regex-like match | ComparisonOperator.LIKE |\n| `not_like(pattern, case_sensitive=True)` | Negative match | ComparisonOperator.NOT_LIKE |\n| `regex_match(pattern)` | `=~` regex match | ComparisonOperator.REGEX_MATCH |\n| `not_regex_match(pattern)` | `!~` negative regex | ComparisonOperator.NOT_REGEX_MATCH |\n\n### Contains/Prefix/Suffix Filters\n\n| Method | Description | Backing Enum |\n|--------|-------------|--------------|\n| `contains_filter(value)` | Contains element/substr | ComparisonOperator.CONTAINS |\n| `starts_with_filter(value, case_sensitive=True)` | Prefix match | ComparisonOperator.STARTS_WITH |\n| `ends_with_filter(value, case_sensitive=True)` | Suffix match | ComparisonOperator.ENDS_WITH |\n| `is_null()` | Field is NULL | ComparisonOperator.IS_NULL |\n| `is_not_null()` | Field is NOT NULL | ComparisonOperator.IS_NOT_NULL |\n\n### Logical Operators (on FilterExpression)\n\n| Operator | Description | Backing |\n|----------|-------------|---------|\n| `&` | Logical AND | FilterExpression.__and__ -> LogicalOperator.AND |\n| `|` | Logical OR | FilterExpression.__or__ -> LogicalOperator.OR |\n| `^` | Logical XOR | FilterExpression.__xor__ -> LogicalOperator.XOR |\n| `~` | Logical NOT | FilterExpression.__invert__ -> NotFilterExpression |\n\n### Arithmetic Operators (on QueryField)\n\n| Operator | Description | Backing |\n|----------|-------------|---------|\n| `+` | Addition / list concatenation | QueryField.__add__/__radd__ -> ArithmeticOperator.ADD |\n| `-` | Subtraction | QueryField.__sub__/__rsub__ -> ArithmeticOperator.SUB |\n| `*` | Multiplication | QueryField.__mul__/__rmul__ -> ArithmeticOperator.MUL |\n| `/` | Division | QueryField.__truediv__/__rtruediv__ -> ArithmeticOperator.DIV |\n| `%` | Modulo | QueryField.__mod__/__rmod__ -> ArithmeticOperator.MOD |\n| `^` | Power | QueryField.__pow__/__rpow__ -> ArithmeticOperator.POW |\n\n### Indexing/Slicing (on QueryField)\n\n| Operator | Description | Backing Function |\n|----------|-------------|------------------|\n| `field[idx]` | 1-based index extract | FunctionExpression(\"array_extract\") |\n| `field[a:b]` | 1-based slice | FunctionExpression(\"array_slice\") |\n\n---\n\n## Model Definition\n\n### Node Models\n\n```python\nfrom kuzualchemy import kuzu_node, KuzuBaseModel, kuzu_field, KuzuDataType\nfrom typing import Optional, List\nfrom datetime import datetime\n\n@kuzu_node(\"User\") # Table name in Kuzu\nclass User(KuzuBaseModel):\n # Primary key\n id: int = kuzu_field(kuzu_type=KuzuDataType.INT64, primary_key=True)\n\n # Basic fields\n name: str = kuzu_field(kuzu_type=KuzuDataType.STRING, not_null=True)\n email: Optional[str] = kuzu_field(kuzu_type=KuzuDataType.STRING, unique=True, default=None)\n age: int = kuzu_field(kuzu_type=KuzuDataType.INT32, default=0)\n\n # Boolean fields\n is_active: bool = kuzu_field(kuzu_type=KuzuDataType.BOOL, default=True)\n\n # Timestamp fields\n created_at: datetime = kuzu_field(\n kuzu_type=KuzuDataType.TIMESTAMP,\n default=KuzuDefaultFunction.CURRENT_TIMESTAMP\n )\n\n # Array fields\n tags: Optional[List[str]] = kuzu_field(\n kuzu_type=ArrayTypeSpecification(element_type=KuzuDataType.STRING),\n default=None\n )\n```\n\n### Relationship Models\n\n```python\nfrom kuzualchemy import kuzu_relationship, KuzuRelationshipBase\n\n@kuzu_relationship(\"KNOWS\", pairs=[(User, User)])\nclass Knows(KuzuRelationshipBase):\n since: datetime = kuzu_field(kuzu_type=KuzuDataType.TIMESTAMP)\n strength: float = kuzu_field(kuzu_type=KuzuDataType.DOUBLE, default=1.0)\n```\n\n### Model Methods\n\nEvery model inherits these methods from `KuzuBaseModel`:\n\n```python\nclass User(KuzuBaseModel):\n # Built-in methods available:\n\n def save(self, session: KuzuSession) -> None:\n \"\"\"Save instance to database\"\"\"\n pass\n\n def delete(self, session: KuzuSession) -> None:\n \"\"\"Delete instance from database\"\"\"\n pass\n\n @classmethod\n def query(cls, session: KuzuSession = None) -> Query:\n \"\"\"Create query for this model\"\"\"\n pass\n\n @classmethod\n def get_primary_key_fields(cls) -> List[str]:\n \"\"\"Get primary key field names\"\"\"\n pass\n\n @classmethod\n def get_foreign_key_fields(cls) -> Dict[str, ForeignKeyMetadata]:\n \"\"\"Get foreign key fields\"\"\"\n pass\n```\n\n---\n\n## Field Types & Metadata\n\n### Supported Kuzu Data Types\n\n```python\nfrom kuzualchemy import KuzuDataType\n\n# Numeric types\nKuzuDataType.INT8, KuzuDataType.INT16, KuzuDataType.INT32, KuzuDataType.INT64\nKuzuDataType.UINT8, KuzuDataType.UINT16, KuzuDataType.UINT32, KuzuDataType.UINT64\nKuzuDataType.FLOAT, KuzuDataType.DOUBLE\nKuzuDataType.DECIMAL, KuzuDataType.SERIAL\n\n# String types\nKuzuDataType.STRING, KuzuDataType.BLOB\n\n# Boolean type\nKuzuDataType.BOOL\n\n# Date/time types\nKuzuDataType.DATE, KuzuDataType.TIMESTAMP, KuzuDataType.INTERVAL\n\n# UUID type\nKuzuDataType.UUID\n\n# Complex types\nKuzuDataType.STRUCT, KuzuDataType.MAP, KuzuDataType.UNION\n```\n\n### Field Definition\n\n```python\n# Field definition\nfield = kuzu_field(\n # Basic properties\n kuzu_type=KuzuDataType.STRING,\n primary_key=False,\n unique=False,\n not_null=False,\n index=False,\n\n # Default values\n default=\"default_value\",\n default_factory=lambda: \"computed_default\",\n\n # Constraints\n check_constraint=\"LENGTH(field_name) > 0\",\n\n # Foreign keys\n foreign_key=ForeignKeyMetadata(\n target_model=TargetModel,\n target_field=\"id\",\n on_delete=CascadeAction.CASCADE,\n on_update=CascadeAction.SET_NULL\n ),\n\n # Metadata\n alias=\"field_alias\",\n title=\"Field Title\",\n description=\"Field description\"\n)\n```\n\n### Array Fields\n\n```python\nfrom kuzualchemy.kuzu_orm import ArrayTypeSpecification\n\nclass User(KuzuBaseModel):\n # Array field definition\n tags: List[str] = kuzu_field(\n kuzu_type=ArrayTypeSpecification(element_type=KuzuDataType.STRING),\n default=None\n )\n```\n\n### Default Values\n\n```python\nfrom kuzualchemy.constants import KuzuDefaultFunction\n\nclass User(KuzuBaseModel):\n # Static defaults\n status: str = kuzu_field(kuzu_type=KuzuDataType.STRING, default=\"active\")\n\n # Function defaults\n created_at: datetime = kuzu_field(\n kuzu_type=KuzuDataType.TIMESTAMP,\n default=KuzuDefaultFunction.CURRENT_TIMESTAMP\n )\n\n # Factory defaults\n uuid_field: str = kuzu_field(\n kuzu_type=KuzuDataType.UUID,\n default_factory=lambda: str(uuid.uuid4())\n )\n```\n\n---\n\n## Relationships\n\n### Basic Relationships\n\n```python\nfrom kuzualchemy import kuzu_relationship, KuzuRelationshipBase\n\n@kuzu_relationship(\"FOLLOWS\", pairs=[(User, User)])\nclass Follows(KuzuRelationshipBase):\n since: datetime = kuzu_field(kuzu_type=KuzuDataType.TIMESTAMP)\n weight: float = kuzu_field(kuzu_type=KuzuDataType.DOUBLE, default=1.0)\n```\n\n### Multi-Pair Relationships\n\n```python\n# Multiple relationship pairs\n@kuzu_relationship(\"AUTHORED\", pairs=[\n (User, {Post, Comment}),\n (Organization, Post)\n])\nclass Authored(KuzuRelationshipBase):\n created_at: datetime = kuzu_field(kuzu_type=KuzuDataType.TIMESTAMP)\n role: str = kuzu_field(kuzu_type=KuzuDataType.STRING, default=\"author\")\n```\n\n### Relationship Usage\n\n```python\n# Create relationships\nuser1 = User(id=1, name=\"Alice\")\nuser2 = User(id=2, name=\"Bob\")\nfollows = Follows(from_node=user1, to_node=user2, since=datetime.now())\n\nsession.add_all([user1, user2, follows])\nsession.commit()\n```\n\n---\n\n## Query System\n\n### Basic Queries\n\n```python\nfrom kuzualchemy import Query\n\n# Create query\nquery = Query(User, session=session)\n\n# Simple filtering\nfiltered = query.where(query.fields.age > 25)\nresults = filtered.all()\n\n# Method chaining\nresults = (Query(User, session=session)\n .where(Query(User, session=session).fields.name.starts_with(\"A\"))\n .where(Query(User, session=session).fields.age.between(20, 40))\n .order_by(Query(User, session=session).fields.name.asc())\n .limit(10)\n .all())\n```\n\n### Advanced Queries\n\n```python\n# Aggregation with HAVING: count users by age > 1\nq = Query(User, session=session)\nagg = q.count() # COUNT(*) AS count\ncount_by_age = (\n q.group_by(q.fields.age)\n .having(ka.to_int64(\"count\") > 1) # compare aggregated alias post-WITH using cast\n)\n\n# Relationship join (pattern: join(TargetModel, RelationshipClass, ...))\nq = Query(User, session=session)\njoined = q.join(User, Follows, target_alias=\"u2\")\n\n# Subquery: authors older than 30\nsubq = Query(User, session=session).where(Query(User, session=session).fields.age > 30)\nmain_query = Query(Post, session=session).where(\n Query(Post, session=session).fields.author_id.in_(subq.select(\"id\"))\n)\n```\n\n### Function Usage in Queries\n\n```python\nimport kuzualchemy as ka\n\n# Text functions\nquery = Query(User, session=session)\ntext_query = query.where(\n ka.upper(query.fields.name).starts_with(\"A\")\n)\n\n# Numeric functions\nnumeric_query = query.where(\n ka.abs(query.fields.age - 30) < 5\n)\n\n# List functions\nlist_query = query.where(\n ka.list_contains(query.fields.hobbies, \"reading\")\n)\n\n# Date functions\ndate_query = query.where(\n ka.date_part(\"year\", query.fields.birth_date) > 1990\n)\n```\n\n### Query Results\n\n```python\n# Get all results\nresults = query.all() # List[ModelType]\n\n# Get first result\nfirst = query.first() # ModelType | None\n\n# Get exactly one result\none = query.one() # ModelType (raises if 0 or >1)\n\n# Get one or none\none_or_none = query.one_or_none() # ModelType | None\n\n# Check existence\nexists = query.exists() # bool\n\n# Count results\ncount_query = query.count() # Query with COUNT(*) AS count aggregation\ncount = count_query._execute()[0][\"count\"] if count_query._execute() else 0\n```\n\n---\n\n## Session Management\n\n### Basic Session Usage\n\n```python\nfrom kuzualchemy import KuzuSession\nfrom pathlib import Path\n\n# Create session\nsession = KuzuSession(db_path=Path(\"my_database.db\"))\n\n# Execute DDL\nddl = get_all_ddl()\nif ddl.strip():\n session.execute(ddl)\n\n# Add and commit\nuser = User(id=1, name=\"Alice\", email=\"alice@example.com\")\nsession.add(user)\nsession.commit()\n\n# Close session\nsession.close()\n```\n\n### Transaction Management\n\n```python\n# Manual transactions using KuzuTransaction\nfrom kuzualchemy import KuzuTransaction\n\nwith KuzuTransaction(session):\n user = User(id=1, name=\"Alice\")\n session.add(user)\n # Automatic commit on success, rollback on exception\n\n# Or using session.begin() context manager\nwith session.begin():\n user = User(id=1, name=\"Alice\")\n session.add(user)\n # Automatic commit on success, rollback on exception\n```\n\n### Session Factory\n\n```python\nfrom kuzualchemy import SessionFactory\n\n# Create factory\nfactory = SessionFactory(\n db_path=\"database.db\",\n autoflush=True,\n autocommit=False\n)\n\n# Create sessions\nsession1 = factory.create_session()\nsession2 = factory.create_session(autocommit=True) # Override defaults\n\n# Session scope context manager\nwith factory.session_scope() as session:\n user = User(id=1, name=\"Alice\")\n session.add(user)\n # Automatic commit/rollback\n```\n\n### Connection Management\n\n```python\nfrom kuzualchemy import KuzuConnection\n\n# Direct connection usage\nconnection = KuzuConnection(db_path=\"database.db\")\nsession = KuzuSession(connection=connection)\n\n# Connection sharing\nsession1 = KuzuSession(connection=connection)\nsession2 = KuzuSession(connection=connection)\n```\n\n---\n\n## Advanced Features\n\n### Registry Management\n\n```python\nfrom kuzualchemy import (\n get_registered_nodes,\n get_registered_relationships,\n get_all_models,\n clear_registry,\n validate_all_models\n)\n\n# Access registered models\nnodes = get_registered_nodes()\nrelationships = get_registered_relationships()\nall_models = get_all_models()\n\n# Validate all models\nvalidation_errors = validate_all_models()\n\n# Clear registry (useful for testing)\nclear_registry()\n```\n\n### Enhanced Base Model with Enum Conversion\n\n```python\nfrom kuzualchemy import BaseModel\nfrom enum import Enum\n\nclass Status(Enum):\n ACTIVE = \"active\"\n INACTIVE = \"inactive\"\n\n@kuzu_node(\"Account\")\nclass Account(BaseModel): # Automatic enum conversion\n status: Status = kuzu_field(kuzu_type=KuzuDataType.STRING)\n\n # BaseModel automatically converts enums to/from string values\n```\n\n### Foreign Key Support\n\n```python\nfrom kuzualchemy import ForeignKeyMetadata, CascadeAction\n\n@kuzu_node(\"Post\")\nclass Post(KuzuBaseModel):\n id: int = kuzu_field(kuzu_type=KuzuDataType.INT64, primary_key=True)\n title: str = kuzu_field(kuzu_type=KuzuDataType.STRING)\n author_id: int = kuzu_field(\n kuzu_type=KuzuDataType.INT64,\n foreign_key=ForeignKeyMetadata(\n target_model=User,\n target_field=\"id\",\n on_delete=CascadeAction.CASCADE\n )\n )\n```\n\n### Custom Functions\n\n```python\n# All Kuzu functions are available as standalone callables\nimport kuzualchemy as ka\n\n# Use in queries\nquery = Query(User, session=session).where(\n ka.concat(query.fields.first_name, \" \", query.fields.last_name).contains(\"Alice\")\n)\n\n# Use in expressions\nfull_name = ka.concat(user.first_name, \" \", user.last_name)\n```\n\n### Complex Expressions\n\n```python\n# Combine multiple functions and operators\ncomplex_filter = (\n ka.upper(query.fields.name).starts_with(\"A\") &\n (query.fields.age.between(20, 40)) &\n ka.list_contains(query.fields.tags, \"python\")\n)\n\nresults = Query(User, session=session).where(complex_filter).all()\n```\n\n---\n\n## API Reference\n\n### Core Classes\n\n#### KuzuBaseModel\nBase class for all node models with built-in ORM functionality.\n\n**Methods:**\n- `save(session: KuzuSession) -> None`: Save instance to database\n- `delete(session: KuzuSession) -> None`: Delete instance from database\n- `query(session: KuzuSession = None) -> Query`: Create query for this model\n- `get_kuzu_metadata(field_name: str) -> KuzuFieldMetadata`: Get field metadata\n- `get_all_kuzu_metadata() -> Dict[str, KuzuFieldMetadata]`: Get all field metadata\n- `get_primary_key_fields() -> List[str]`: Get primary key field names\n- `get_foreign_key_fields() -> Dict[str, ForeignKeyMetadata]`: Get foreign key fields\n\n#### KuzuRelationshipBase\nBase class for relationship models.\n\n**Methods:**\n- Same as KuzuBaseModel plus relationship-specific functionality\n- `create_between(from_node, to_node, **properties) -> KuzuRelationshipBase`: Factory to instantiate relationship between nodes\n- `from_node_pk`/`to_node_pk` properties for node primary keys\n\n#### KuzuSession\nMain session class for database operations.\n\n**Methods:**\n- `execute(query: str, parameters: Dict = None) -> List[Dict]`: Execute raw query\n- `add(instance: Any) -> None`: Add instance to session\n- `add_all(instances: List[Any]) -> None`: Add multiple instances\n- `delete(instance: Any) -> None`: Mark instance for deletion\n- `commit() -> None`: Commit current transaction\n- `rollback() -> None`: Rollback current transaction\n- `flush() -> None`: Flush pending changes\n- `close() -> None`: Close session\n- `begin() -> KuzuTransaction`: Begin transaction context\n\n#### Query[ModelType]\nType-safe query builder.\n\n**Methods:**\n- `where(expression: FilterExpression) -> Query`: Add WHERE clause\n- `filter(*expressions: FilterExpression) -> Query`: Add multiple filters\n- `order_by(*fields: QueryField) -> Query`: Add ORDER BY clause\n- `group_by(*fields: QueryField) -> Query`: Add GROUP BY clause\n- `having(expression: FilterExpression) -> Query`: Add HAVING clause\n- `limit(count: int) -> Query`: Add LIMIT clause\n- `offset(count: int) -> Query`: Add OFFSET clause\n- `distinct() -> Query`: Add DISTINCT clause\n- `all() -> List[ModelType]`: Execute and return all results\n- `first() -> ModelType | None`: Execute and return first result\n- `one() -> ModelType`: Execute and return exactly one result\n- `one_or_none() -> ModelType | None`: Execute and return one or none\n- `count() -> int`: Count results\n- `exists() -> bool`: Check if results exist\n\n### Field Definition\n\n#### kuzu_field Function\nField definition with options:\n\n```python\nkuzu_field(\n default: Any = ..., # Default value\n kuzu_type: Union[KuzuDataType, str, ArrayTypeSpecification], # Kuzu data type\n primary_key: bool = False, # Primary key flag\n foreign_key: ForeignKeyMetadata = None, # Foreign key metadata\n unique: bool = False, # Unique constraint\n not_null: bool = False, # NOT NULL constraint\n index: bool = False, # Index flag\n check_constraint: str = None, # CHECK constraint\n default_factory: Callable[[], Any] = None, # Default factory function\n alias: str = None, # Field alias\n title: str = None, # Field title\n description: str = None, # Field description\n)\n```\n\n### Decorators\n\n#### @kuzu_node()\nMark class as Kuzu node:\n\n```python\n@kuzu_node(\n name: str = None, # Node name (defaults to class name)\n abstract: bool = False, # Abstract node flag\n compound_indexes: List[CompoundIndex] = None, # Compound indexes\n table_constraints: List[str] = None, # Table constraints\n properties: Dict[str, Any] = None # Additional properties\n)\n```\n\n#### @kuzu_relationship()\nMark class as Kuzu relationship:\n\n```python\n@kuzu_relationship(\n name: str = None, # Relationship name\n pairs: List[Tuple[Type, Type]] = None, # Valid node pairs\n multiplicity: RelationshipMultiplicity = MANY_TO_MANY, # Relationship multiplicity\n compound_indexes: List[CompoundIndex] = None, # Compound indexes\n table_constraints: List[str] = None, # Table constraints\n properties: Dict[str, Any] = None # Additional properties\n)\n```\n\n### Enums and Constants\n\n#### KuzuDataType\nAll supported Kuzu data types:\n- Numeric: `INT8`, `INT16`, `INT32`, `INT64`, `UINT8`, `UINT16`, `UINT32`, `UINT64`, `FLOAT`, `DOUBLE`, `DECIMAL`, `SERIAL`\n- String: `STRING`, `BLOB`\n- Boolean: `BOOL`\n- Temporal: `DATE`, `TIMESTAMP`, `INTERVAL`\n- Other: `UUID`, `STRUCT`, `MAP`, `UNION`\n\n#### ComparisonOperator\nQuery comparison operators (from kuzu_query_expressions):\n- `EQ`, `NEQ`: Equality/inequality\n- `LT`, `LTE`, `GT`, `GTE`: Comparison operators\n- `IN`, `NOT_IN`: List membership\n- `LIKE`, `NOT_LIKE`: Pattern matching\n- `IS_NULL`, `IS_NOT_NULL`: Null checks\n- `CONTAINS`: String/array contains\n- `STARTS_WITH`, `ENDS_WITH`: String prefix/suffix\n- `EXISTS`, `NOT_EXISTS`: Existence checks\n\n#### LogicalOperator\nLogical operators for combining conditions:\n- `AND`, `OR`, `NOT`, `XOR`: Boolean logic\n\n#### AggregateFunction\nAggregate functions:\n- `COUNT`, `COUNT_DISTINCT`: Counting\n- `SUM`, `AVG`: Numeric aggregation\n- `MIN`, `MAX`: Extrema\n- `COLLECT`, `COLLECT_LIST`, `COLLECT_SET`: Collection aggregation\n\n#### OrderDirection\nOrdering directions:\n- `ASC`: Ascending order\n- `DESC`: Descending order\n\n#### JoinType\nJoin types:\n- `INNER`: Inner join\n- `OPTIONAL`: Optional match (left outer join)\n\n### Utility Functions\n\n#### DDL Generation\n- `get_all_ddl() -> str`: Generate DDL for all registered models\n- `get_ddl_for_node(node_cls: Type[Any]) -> str`: Generate DDL for specific node\n- `get_ddl_for_relationship(rel_cls: Type[Any]) -> str`: Generate DDL for specific relationship\n\n#### Registry Management\n- `get_registered_nodes() -> Dict[str, Type[Any]]`: Get all registered nodes\n- `get_registered_relationships() -> Dict[str, Type[Any]]`: Get all registered relationships\n- `get_all_models() -> Dict[str, Type[Any]]`: Get all registered models\n- `clear_registry() -> None`: Clear model registry\n- `validate_all_models() -> List[str]`: Validate all registered models\n\n#### Test Utilities\n- `initialize_schema(session: KuzuSession, ddl: str = None) -> None`: Initialize database schema\n\n---\n\n---\n\n## Contributing\n\nWe welcome contributions to KuzuAlchemy! Please see our [Contributing Guide](CONTRIBUTING.md) for details.\n\n### Development Setup\n\n```bash\n# Clone repository\ngit clone <repository-url>\ncd kuzualchemy\n\n# Install in development mode\npip install -e \".[dev,test]\"\n\n# Run tests\npytest\n\n# Run type checking\nmypy src/\n\n# Run linting\nflake8 src/\nblack src/\n\n# Build package\npython -m build\n```\n\n### Testing\n\n```bash\n# Run all tests\npytest\n\n# Run specific test categories\npytest tests/test_functions.py\npytest tests/test_integration.py\n\n# Run with coverage\npytest --cov=kuzualchemy --cov-report=html\n```\n\n---\n\n## License\n\nThis project is licensed under the GPL-3.0 license - see the [LICENSE](LICENSE) file for details.\n\n---\n\n## Conclusion\n\nKuzuAlchemy is an Object-Relational Mapping library for the Kuzu graph database. It provides a SQLAlchemy-like interface for working with graph data.\n",
"bugtrack_url": null,
"license": "GPL-3.0",
"summary": "SQLAlchemy-like ORM for Kuzu graph database",
"version": "0.2.0",
"project_urls": {
"Bug Tracker": "https://github.com/FanaticPythoner/kuzualchemy/issues",
"Documentation": "https://kuzualchemy.readthedocs.io",
"Homepage": "https://github.com/FanaticPythoner/kuzualchemy",
"Repository": "https://github.com/FanaticPythoner/kuzualchemy"
},
"split_keywords": [
"kuzu",
" graph",
" database",
" orm",
" sqlalchemy",
" cypher",
" graph-database"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "f79ed13a3ec234c2297d8c8280e01dc1c86b24ac42c5a28528df0b2bed9e2361",
"md5": "57b858a58af1f6ffbd3c15cec004eae0",
"sha256": "299e5fea1cf2e14996d4346f67b26f38a743751adbfd4d9f61525e26a09add49"
},
"downloads": -1,
"filename": "kuzualchemy-0.2.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "57b858a58af1f6ffbd3c15cec004eae0",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 106634,
"upload_time": "2025-09-04T18:35:21",
"upload_time_iso_8601": "2025-09-04T18:35:21.966500Z",
"url": "https://files.pythonhosted.org/packages/f7/9e/d13a3ec234c2297d8c8280e01dc1c86b24ac42c5a28528df0b2bed9e2361/kuzualchemy-0.2.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "1ddbe93e2f5c8185dde067626d26c0946081f5989141069e7346055284fa8e2e",
"md5": "6cd8df231d4fe1821c15c1ecfb5b7f06",
"sha256": "52d59a066326e7d6823d6d375771352e1c1c3c3d4a2f9b5b7022984c636b3552"
},
"downloads": -1,
"filename": "kuzualchemy-0.2.0.tar.gz",
"has_sig": false,
"md5_digest": "6cd8df231d4fe1821c15c1ecfb5b7f06",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 125007,
"upload_time": "2025-09-04T18:35:23",
"upload_time_iso_8601": "2025-09-04T18:35:23.421757Z",
"url": "https://files.pythonhosted.org/packages/1d/db/e93e2f5c8185dde067626d26c0946081f5989141069e7346055284fa8e2e/kuzualchemy-0.2.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-09-04 18:35:23",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "FanaticPythoner",
"github_project": "kuzualchemy",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "kuzualchemy"
}