Breaking Changes
- Minimum Neo4j / Bolt raised (#292, #293) — bumps to
bolty0.1.0, which speaks Bolt 5.6–6.0, so Neo4j 4.x (Bolt 4.x) is no longer supported. Flagged as breaking for completeness, but it should break no one in practice: Neo4j 4.x is end-of-life and not considered secure, so no realistic ash_neo4j deployment targets it.
Features
-
Vector embeddings & similarity search (#74) — a new
AshNeo4j.Types.Vectorattribute type, stored as a Neo4jLIST<FLOAT>, withvector_similarity(cosine) andvector_cosine_distanceAsh query functions, k-NN ordering pushed down to Cypher, andAshNeo4j.Vectorindex-lifecycle helpers. Vector predicates and ordering route to a Cypher-25-capable pool. Because the values persist asLIST<FLOAT>(the nativeVECTORtype can't be a node property), the feature is gated on Cypher 25. -
WGS-84-3D points (#270, Phase 1) —
%Geo.PointZ{}(srid 4979) stores as a native 3D Neo4jPOINTat<attr>.point, withpoint.distancepushdown in 3D and an in-memory haversine (mean-height-scaled arc + Δh) that matches Neo4j to ~0.1 m. A strict dimension policy stops 2D and 3D mixing silently — a mismatch raisesAshNeo4j.Error.GeoDimensionMismatch, andAshNeo4j.Geo.force_2d/1does an explicit downward projection. 3D areal/linear geometry (PolygonZ, …) raisesAshNeo4j.Error.Unsupported3DGeometry, deferred to Phase 2. -
CYPHER 25 language selector (#292, #293) — on Neo4j ≥ 2025.06, AshNeo4j auto-prepends
CYPHER 25to generated queries, opting into the versioned Cypher 25 language; older servers stay on Cypher 5. The selector is derived from the server version and cached per pool, and is distinct from the Bolt protocol version.
Bug Fixes
-
Spatial POINT index now effective at scale (#311) — two issues left geospatial queries unindexed despite a POINT index existing. A
:point_zattribute built indexes on the.bbSW/.bbNEcompanions a%Geo.PointZ{}never writes (it now indexes the.pointit stores), and thewithin_bbox/within_bbox_boxcontainment form put the indexed properties in the box position, forcing aNodeByLabelScan. The containment predicates are reformulated into range scans on the indexed corners (NodeIndexSeekByRange). Benchmarked at N=10k: indexedst_dwithin~6–7×; point-in-polygon containment is index-servable but caps near ~1.3× (single-corner seek). -
existsover an empty result isfalse, notnil(#301). -
CYPHER 25 selector emitted once (#299) — inside a
CALL {…}combination block the selector was prepended per branch and again on the outer query; it is now emitted only once, on the outer query. -
Root-node aggregates (#291) — an aggregate with no relationship path now aggregates over the root node, not an unbound relationship variable.
-
Domain-fragment label resolution (#295) — uses
Code.ensure_compiled/1so a domain-fragment label resolves under any compilation order. -
Test-suite stability (#304) — caps ExUnit
max_casesto the Bolt pool size, removing intermittentDBConnection:queue_timeoutfailures under parallel tests (test-only; no runtime effect).