Changelog¶
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[1.3.0] - 2026-04-01¶
Added¶
- Aggregation functions (#1):
math_mean(),math_sum(),math_max(),math_min()for SurrealQL aggregation queries. Compose withas_()aliases andgroup_by()/group_all()clauses. - Query builder GROUP ALL (#1):
group_all()method onQueryfor full-table aggregation. - Record references (#2):
record_ref(table, id)generatestype::record('table', 'id')expressions that render as raw SurrealQL in CRUD operations, not quoted as strings. - SurrealDB function values (#3):
surql_fn(name, *args)for passing server-side functions (time::now(), math::sum, etc.) as field values in CREATE/UPDATE operations. - Result extraction integration tests (#4): Comprehensive tests for
extract_result(),extract_one(),extract_scalar()against realistic SurrealDB response formats.
1.2.1 - 2026-03-20¶
Fixed¶
- RecordID round-trip denormalization: Added
_denormalize_params()that recursively converts record ID strings (e.g.'repo:abc123') back tosurrealdb.RecordIDobjects before sending to the SDK. Applied tocreate(),update(),merge(),execute(), andinsert_relation()input data/params. This fixes the round-trip where normalized response IDs (strings) were rejected by SurrealDB 3.x when passed back as field values in subsequent operations ("Expectedrecordbut found string")
Testing¶
- Added 18 tests covering
_denormalize_paramsunit behavior, round-trip identity, and input denormalization verification across all CRUD methods
1.2.0 - 2026-03-20¶
Fixed¶
- select() single-record unwrap:
DatabaseClient.select()now detects record ID targets (e.g.user:alice) and unwraps the single-element list returned by the SurrealDB 3.x SDK, returning a dict (or None) instead of a list for single-record selects - SDK RecordID normalization: All
DatabaseClientCRUD responses (create,select,update,merge,delete,execute,insert_relation) now recursively normalize SurrealDB SDKRecordIDobjects to plain strings, preventing type coercion errors when consumers pass returned IDs back as field values in subsequent operations
Testing¶
- Added 38 tests covering
_is_record_id_target,_normalize_sdk_value, single-record select unwrapping, and SDK type normalization across all CRUD methods
1.1.0 - 2026-03-19¶
Added¶
- HNSW vector indexes:
IndexType.HNSWfor SurrealDB's HNSW (Hierarchical Navigable Small World) approximate nearest-neighbor index, the successor to MTREE in SurrealDB 2.x/3.x - HnswDistanceType enum: 8 distance metrics -- CHEBYSHEV, COSINE, EUCLIDEAN, HAMMING, JACCARD, MANHATTAN, MINKOWSKI, PEARSON (superset of MTreeDistanceType)
- hnsw_index() builder: Convenience function for creating HNSW index definitions with dimension, distance metric, vector type, and optional EFC/M tuning parameters
- HNSW SQL generation:
generate_table_sql()andgenerate_edge_sql()emit correctDEFINE INDEX ... HNSW DIMENSION <n> DIST <dist> TYPE <type> [EFC <n>] [M <n>]syntax - HNSW parsing: Schema parser detects and extracts HNSW indexes with all parameters (dimension, distance, vector type, EFC, M) from SurrealDB INFO responses
- HNSW validation: Schema validator checks HNSW dimension, distance metric, vector type, EFC, and M parameters for code-vs-database consistency
- HNSW migration diffs:
diff_indexes()generates correct forward/backward SQL for adding and dropping HNSW indexes - HNSW example:
docs/examples/hnsw_vector_search.pydemonstrating HNSW usage with OpenAI embeddings, multiple distance types, and EFC/M tuning
Testing¶
- Test coverage: 2215 tests passing (up from 2191 in 1.0.0), 9 skipped
- New test suite:
test_hnsw_diff.py(24 tests covering SQL generation, add/drop diffs, all 8 distance types, EFC/M parameters, mixed index type diffs, error cases)
1.0.0 - 2026-03-13¶
Added¶
- Vector search threshold:
vector_search()now accepts athresholdparameter for MTREE similarity filtering, generating<|K,DISTANCE,threshold|>syntax - Similarity scoring:
similarity_score()method on Query addsvector::similarity::{metric}(field, vector) AS aliasto SELECT fields - similarity_search_query(): Convenience function combining
vector_search()andsimilarity_score()for common vector search patterns (replaces manual SurrealQL construction in consumer projects)
Fixed¶
- Edge diff returns empty for modified edges:
diff_edges()now compares fields, indexes, events, and permissions when both old and new edges exist (previously returned an empty list with a TODO comment) - Event condition/action SQL injection: Added
_validate_event_expression()that rejects statement separators and SQL comments before interpolation into generated SQL - Permission rollback SQL always empty:
_generate_modify_permissions_diff()now generates rollback SQL from old permissions instead of always producing empty backward SQL - Bare exception blocks: Narrowed 9 bare
except Exception:blocks across migration/ and connection/ modules to specific exception types
Changed¶
- Project renamed:
reverie->surql(PyPI:oneiriq-surql, import:surql). Unified branding with the TypeScript SurrealDB toolkit under the Oneiriq org - Version 1.0.0: First stable release. Development Status upgraded from Alpha to Production/Stable
- CLI command:
reverie->surql(e.g.,surql migrate up,surql schema show) - Settings section:
[tool.reverie]->[tool.surql]in pyproject.toml - Cache key prefix:
reverie:->surql:by default - Split cli/schema.py (1954 LOC): Extracted into
schema_inspect.py,schema_diff.py,schema_validate.py,schema_watch.py,schema_visualize.pywith thin command wrappers - Split cli/migrate.py (1232 LOC): Extracted into
migrate_core.py,migrate_squash.py,migrate_advanced.pywith thin command wrappers - Split schema/validator.py (1029 LOC): Extracted utility functions into
schema/validator_utils.py. All files now comply with the 1000 LOC limit
Testing¶
- Test coverage: 2191 tests passing (up from 2161 in 0.8.0)
- New test suites:
test_edge_diff.py(edge diff, event validation, permission rollback) - Extended:
test_query.pywith vector threshold and similarity scoring tests
0.8.0 - 2026-03-11¶
Added¶
- Typed Pydantic CRUD:
create_typed(),get_typed(),query_typed(),update_typed(),upsert_typed()functions that accept Pydantic model types and return validated model instances instead of raw dicts - DEFINE ACCESS support:
AccessDefinition,AccessType,JwtConfig,RecordAccessConfigschema types withaccess_schema(),jwt_access(),record_access()builders andgenerate_access_sql()for SurrealQL generation - IF NOT EXISTS support:
if_not_existsparameter ongenerate_table_sql(),generate_edge_sql(), andgenerate_schema_sql()for idempotent schema migrations - Reserved word validation:
check_reserved_word()andSURREAL_RESERVED_WORDSfor detecting field names that collide with SurrealDB reserved words (emits warnings, not errors)
Changed¶
- Split query/builder.py: Extracted
ReturnFormatenum and 12 standalone free functions intoquery/helpers.py, bringing builder.py from 1137 to 947 LOC - Split query/graph.py: Extracted
GraphQueryclass intoquery/graph_query.py, bringing graph.py from 1151 to 794 LOC. All files now comply with the 1000 LOC limit
Testing¶
- Test coverage: 2161 tests passing (up from 2089 in 0.7.0)
- New test suites:
test_typed_crud.py,test_access.py,test_reserved_words.py - Extended:
test_schema_sql.pywith IF NOT EXISTS tests
0.7.0 - 2026-03-11¶
Added¶
- Upsert support:
upsert()query builder method andupsert_record()CRUD function for insert-or-update operations - Datetime coercion utilities:
coerce_datetime()andcoerce_record_datetimes()for converting SurrealDB ISO datetime strings to Python datetime objects, including nanosecond truncation and timezone handling - SQL generation from schema definitions:
generate_table_sql(),generate_edge_sql(), andgenerate_schema_sql()for generating SurrealQL DEFINE statements directly from TableDefinition/EdgeDefinition objects - Additional exports:
extract_result,extract_one,extract_scalar,has_results, anddelete_recordsadded to package-level__all__ - Field name validation: Schema field builder functions now validate field names against SurrealDB identifier rules (alphanumeric + underscore, dot notation for nested fields)
Fixed¶
- CI format check was a no-op:
ruff format src tests(formats in-place, always passes) changed toruff format --check src tests - GraphQuery.exists() mutated state:
exists()modifiedself._limitdirectly, violating immutability. Rewritten to usecount()without mutation - SQL injection in migration diff defaults: Field default values interpolated into SQL without sanitization. Added
_validate_default_value()with safe literal pattern matching - Pytest marker mismatch: Declared
asynciomarker but tests useanyio. Fixed marker declaration toanyio - Migration executor lacked transactional wrapping: Statements now execute within BEGIN/COMMIT/CANCEL TRANSACTION blocks for atomicity
- Connection client reconnection: Calling
connect()when already connected now properly disconnects first before reconnecting - Cache TTL logic: Fixed unreachable code path for custom TTL tracking
- RecordID empty parts:
parse()now validates that table and id parts are non-empty - Nested transaction prevention: Transaction manager checks for active transactions via
ContextVarand raisesTransactionErrorif nested - asyncio/trio incompatibility: Replaced
asyncio.sleep,asyncio.gather,asyncio.Semaphore, andasyncio.create_taskwith anyio equivalents in orchestration strategies and streaming module
Changed¶
- Edge schema RELATION mode validation: Moved from construction-time to SQL generation time, allowing incremental composition via
with_from_table()/with_to_table() - CHANGES file: Updated to reflect versions 0.1.0 through 0.7.0
Testing¶
- Test coverage: 2089 tests passing (up from ~1018)
- New test suites:
test_coerce.py,test_schema_sql.py,test_upsert.py - Field name validation tests added to
test_schema.py
0.1.0 - 2026-01-02¶
Added¶
- SurrealDB Compatibility: Complete compatibility with common SurrealDB patterns achieved
-
Result extraction utilities for handling SurrealDB response formats
extract_result()- Extract data from nested/flat result formatsextract_one()- Get first record or Noneextract_scalar()- Extract aggregate values (COUNT, SUM, AVG, etc.)has_results()- Check if result contains records- Location:
src/query/results.py:356-514
-
RecordID angle bracket support for complex IDs
- Support for
table:⟨complex-id⟩format required by SurrealDB - Compatible with domain-based IDs like
outlet:⟨alaskabeacon.com⟩ - Compatible with compound IDs like
document:⟨domain:ulid⟩ - Location:
src/types/record_id.py:58-77
- Support for
-
SCHEMAFULL edge table support
EdgeMode.SCHEMAFULLfor traditional edge tables with explicit in/out fieldsschemafull_edge()helper function for traditional edge definitions- Compatible with entity_relation pattern
- Location:
src/schema/edge.py:11-157
-
Example implementations
docs/examples/mtree_vector_search.py- MTREE vector indexes (1024-dim, COSINE)docs/examples/schemafull_edge_example.py- SCHEMAFULL edge table patterns
Fixed¶
- MTREE Index SQL Generation: Changed from incorrect
FIELDSkeyword to correctCOLUMNSkeyword - Previous (incorrect):
DEFINE INDEX ... ON TABLE ... FIELDS embedding MTREE ... - Current (correct):
DEFINE INDEX ... ON TABLE ... COLUMNS embedding MTREE ... - Ensures compatibility with SurrealDB 1.0+ MTREE syntax
- Location:
src/schema/table.py:393 -
Tests:
tests/test_mtree_diff.py -
AsyncSurreal Client Implementation: Verified correct usage of AsyncSurreal for async operations
- Ensures all database operations use proper async/await patterns
- Connection pooling and retry logic function correctly
- Location:
src/connection/client.py:9, 89
Changed¶
- RecordID Validation: Enhanced to support both standard and angle bracket formats
- Standard format:
table:id(alphanumeric + underscores) - Angle bracket format:
table:⟨complex-id⟩(any valid SurrealDB ID) - Backward compatible with existing code
- Location:
src/types/record_id.py
Testing¶
- Test Coverage: 447 tests passing
- Connection management (async operations, pooling, retry logic)
- Schema definition (tables, fields, indexes, edges)
- MTREE indexes (SQL generation, diff detection)
- RecordID validation (standard and angle bracket formats)
- Result extraction (nested/flat formats, aggregates)
- CRUD operations (create, read, update, delete)
- Query building (select, where, order, limit)
- Migration system (up/down, history tracking)
- Edge tables (TYPE RELATION and SCHEMAFULL modes)
- CLI commands (migrate, schema, db)