# `AshNeo4j.Constraint`
[🔗](https://github.com/diffo-dev/ash_neo4j/blob/v0.10.1/lib/constraint.ex#L5)

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`.

# `constraint_statements`

```elixir
@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`

```elixir
@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`

```elixir
@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`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
