AshNeo4j.Constraint (AshNeo4j v0.10.0)

Copy Markdown View Source

Convenience helpers for creating the Neo4j uniqueness constraints that enforce a resource's primary key (#32) and its identities (#20) at the database level.

# Create the constraints for a resource (primary key + every identity)
AshNeo4j.Constraint.create_constraints(AssignmentRelationship)

# Review the Cypher without touching the database
AshNeo4j.Constraint.constraint_statements(AssignmentRelationship)

The primary-key constraint enforces uniqueness of the primary key among nodes of the resource's label. It's skipped when an identity already constrains the same attributes (no redundant constraint).

Consistent with AshNeo4j's "no automatic migrations" stance — like AshNeo4j.Vector, this is an ergonomic tool you call (e.g. from a start-up or release task), not run on boot. Statements use IF NOT EXISTS, so they are safe to run repeatedly.

Unsupported identities are refused, not skipped

An identity that can't be enforced — nils_distinct?: false, or a filtered identity (where:) — returns {:error, %AshNeo4j.Error.UnsupportedIdentity{}} and creates nothing for the resource (all-or-nothing). Skipping would leave the identity unenforced and permit the duplicate records the constraint exists to prevent. The same cases are refused at compile time by AshNeo4j.Verifiers.VerifyIdentities.

Naming

Identity constraints are named <label_lower>_<identity_name> (e.g. assignmentrelationship_unique_assignment); the primary-key constraint is <label_lower>_pk.

Summary

Functions

Returns {:ok, [statement]} — the CREATE CONSTRAINT Cypher create_constraints/2 would run — without touching the database, or {:error, %UnsupportedIdentity{}}.

Runs CREATE CONSTRAINT … IF NOT EXISTS for resource's primary key and every identity.

Runs DROP CONSTRAINT … IF EXISTS for resource's primary key and every identity. A no-op for absent constraints. Unsupported identities are refused (nothing was created for them to drop), consistent with create_constraints/2.

Functions

constraint_statements(resource)

@spec constraint_statements(Ash.Resource.t()) ::
  {:ok, [String.t()]} | {:error, term()}

Returns {:ok, [statement]} — the CREATE CONSTRAINT Cypher create_constraints/2 would run — without touching the database, or {:error, %UnsupportedIdentity{}}.

AshNeo4j.Constraint.constraint_statements(Post)
#=> {:ok, ["CREATE CONSTRAINT post_pk IF NOT EXISTS FOR (n:Post) REQUIRE n.uuid IS UNIQUE",
#         "CREATE CONSTRAINT post_unique_unique IF NOT EXISTS FOR (n:Post) REQUIRE n.unique IS UNIQUE"]}

create_constraints(resource, opts \\ [])

@spec create_constraints(
  Ash.Resource.t(),
  keyword()
) :: {:ok, [Bolty.Response.t()]} | {:error, term()}

Runs CREATE CONSTRAINT … IF NOT EXISTS for resource's primary key and every identity.

Returns {:ok, [%Bolty.Response{}]} (one per constraint), or {:error, %AshNeo4j.Error.UnsupportedIdentity{}} if any identity can't be enforced — in which case nothing is created.

drop_constraints(resource, opts \\ [])

@spec drop_constraints(
  Ash.Resource.t(),
  keyword()
) :: {:ok, [Bolty.Response.t()]} | {:error, term()}

Runs DROP CONSTRAINT … IF EXISTS for resource's primary key and every identity. A no-op for absent constraints. Unsupported identities are refused (nothing was created for them to drop), consistent with create_constraints/2.