Migration helpers for Apache AGE graph database.
Provides functions to create and drop AGE graphs, labels, and indexes within Ecto migrations.
Usage
defmodule MyApp.Repo.Migrations.CreateAgeGraph do
use Ecto.Migration
import AshAge.Migration
def up do
create_age_graph("my_graph")
create_vertex_label("my_graph", "Entity")
create_edge_label("my_graph", "RELATES_TO")
create_vertex_index("my_graph", "Entity", "tenant_id")
end
def down do
drop_age_graph("my_graph")
end
end
Summary
Functions
Creates an AGE graph with the given name.
Creates an index on an edge property.
Creates an edge label in the given graph.
Creates an index on a vertex property.
Creates a vertex label in the given graph.
Drops an AGE graph and all its data.
Resource-derived enable_tenant_rls: reads graph, label, tenant property, and GUC
from the resource's age/multitenancy DSL. The drift-free default — the policy
it writes always matches what the data layer sets at runtime.
Idempotently enables DB-enforced RLS on a resource's label table (host-invoked,
runtime SQL). Emits ENABLE + FORCE ROW LEVEL SECURITY, a functional btree index
on the tenant discriminator, and an expression policy over properties (NOT a
generated column — those segfault AGE cypher() writes). The policy is
fail-closed: a blank/unset GUC (current_setting(guc,true) = '') matches nothing.
Idempotently provisions a tenant's AGE graph and its vertex/edge labels at runtime (host-invoked in a tenant-onboarding flow, or from a migration).
Functions
@spec create_age_graph(String.t()) :: :ok
Creates an AGE graph with the given name.
Idempotent — checks ag_catalog.ag_graph before creating.
Creates an index on an edge property.
Uses ag_catalog.agtype_access_operator() instead of the ->> operator
because public comes before ag_catalog in the search path.
Creates an edge label in the given graph.
Idempotent — checks ag_catalog.ag_label before creating.
Creates an index on a vertex property.
Uses ag_catalog.agtype_access_operator() instead of the ->> operator
because public comes before ag_catalog in the search path.
Creates a vertex label in the given graph.
Idempotent — checks ag_catalog.ag_label before creating.
@spec drop_age_graph(String.t()) :: :ok
Drops an AGE graph and all its data.
Resource-derived enable_tenant_rls: reads graph, label, tenant property, and GUC
from the resource's age/multitenancy DSL. The drift-free default — the policy
it writes always matches what the data layer sets at runtime.
Idempotently enables DB-enforced RLS on a resource's label table (host-invoked,
runtime SQL). Emits ENABLE + FORCE ROW LEVEL SECURITY, a functional btree index
on the tenant discriminator, and an expression policy over properties (NOT a
generated column — those segfault AGE cypher() writes). The policy is
fail-closed: a blank/unset GUC (current_setting(guc,true) = '') matches nothing.
Read/target-side only: AGE cypher() CREATE bypasses WITH CHECK, so cross-tenant
INSERT is not RLS-denied (the :attribute app-layer force-set owns that). The DB
role must be a non-superuser without BYPASSRLS or RLS silently no-ops.
Prefer enable_tenant_rls/2, which derives every argument from the resource DSL.
Idempotently provisions a tenant's AGE graph and its vertex/edge labels at runtime (host-invoked in a tenant-onboarding flow, or from a migration).
Unlike the create_* helpers above (which use Ecto.Migration.execute/1 and
only run inside a migration), this uses Ecto.Adapters.SQL.query!/3, so it
works at runtime too. graph_name and every label are validated as AGE
identifiers before interpolation — the intended caller derives graph_name
from AshAge.tenant_graph/2 over adversarial tenant input.
opts:
:vlabels— vertex labels to create (default[]):elabels— edge labels to create (default[])
Idempotent: guarded by IF NOT EXISTS against ag_catalog, so re-runs are
no-ops.