AshNeo4j.DataLayer (AshNeo4j v0.10.0)

Copy Markdown View Source

Ash DataLayer for Neo4j.

Errors (#372)

Every error the behaviour callbacks (create/update/destroy/run_query/…) return is a typed struct — never a bare string (a string becomes Ash.Error.Unknown.UnknownError: unclassified and only substring-matchable). Pick by meaning:

  • Server/Bolt errorAshNeo4j.Error.Neo4j (classifies by category).
  • Deliberate refusal ("can't push this down / won't render it") → the AshNeo4j.Error.Unsupported* / Requires* family (class: :invalid).
  • The node/edge we expected to act on wasn't there → reuse Ash's own: Ash.Error.Changes.StaleRecord (the record is gone / a guard no longer holds) or Ash.Error.Invalid.Unavailable (a preservation guard blocked a destroy).
  • An internal invariant we couldn't satisfy (unexpected input shape, a relationship/aggregate path that won't resolve) → AshNeo4j.Error.Internal (class: :unknown).

Two standing rules: reuse an Ash error before inventing an AshNeo4j.Error.*, and return, never raise, from the behaviour path. Internal sentinels between helpers (e.g. {:error, :nothing_deleted} from run_expecting_deletions/2) are atoms, not strings, and are converted to a typed error before escaping. The contract is enforced by test/error_contract_test.exs.

Ash.Type callbacks (cast/dump/load) are a separate contract — they follow Ash's type-error convention (message strings), not this one.

Summary

Functions

Read-time polymorphic projection (#329): for each source record, follows chain to the reached node(s) in one query (related_nodes/4) and projects each reached node to its concrete world via AshNeo4j.worlds/1.

Functions

cast_attribute(resource, name, value)

neo4j(body)

(macro)

project_traversal(resource, records, chain)

@spec project_traversal(module(), [Ash.Resource.record()], list()) :: %{
  optional(any()) => any()
}

Read-time polymorphic projection (#329): for each source record, follows chain to the reached node(s) in one query (related_nodes/4) and projects each reached node to its concrete world via AshNeo4j.worlds/1.

Returns %{source_pk_value => projected} where projected is the concrete record, an AshNeo4j.Unknown (a node was reached but its labels resolve to no loaded world), or nil (genuinely nothing reached). v1 is single-valued.