AncestryEx.Integrity (ancestry_ex v0.1.0)

Copy Markdown View Source

Maintenance functions: integrity checks and repairs, building the ancestry column from an adjacency list (parent_id), and rebuilding caches.

They operate on the whole table and are meant for migrations, console sessions or periodic jobs. All of them accept a :prefix option.

Summary

Functions

Fills the ancestry column (and the depth and counter caches) from an adjacency list column, with a single recursive query.

Checks the ancestry of every node of schema.

Recomputes the counter cache column by counting the children of every node. Does nothing unless the schema uses :counter_cache.

Recomputes the depth cache column from the ancestry column. Does nothing unless the schema uses cache_depth: true.

Repairs the ancestry column of schema.

Types

issue()

@type issue() ::
  {:invalid_ancestry, id :: term(), ancestry :: term()}
  | {:cyclic_ancestry, id :: term()}
  | {:missing_ancestor, id :: term(), ancestor_id :: term()}
  | {:conflicting_ancestry, id :: term(), parent_id :: term()}

Functions

build_ancestry_from_parent_ids(repo, schema, opts \\ [])

@spec build_ancestry_from_parent_ids(Ecto.Repo.t(), module(), keyword()) ::
  {:ok, non_neg_integer()}

Fills the ancestry column (and the depth and counter caches) from an adjacency list column, with a single recursive query.

Useful to migrate a table that used a parent_id column. Nodes whose parent does not exist, or that are part of a cycle, are left untouched.

Options

  • :parent_column - the column holding the parent id, :parent_id by default.
  • :prefix - the schema prefix.

Returns {:ok, number_of_updated_nodes}.

check(repo, schema, opts \\ [])

@spec check(Ecto.Repo.t(), module(), keyword()) :: :ok | {:error, [issue()]}

Checks the ancestry of every node of schema.

Returns :ok or {:error, issues} where each issue is one of:

  • {:invalid_ancestry, id, ancestry} - the value is not well formed,
  • {:cyclic_ancestry, id} - the node appears in its own ancestry,
  • {:missing_ancestor, id, ancestor_id} - an ancestor does not exist,
  • {:conflicting_ancestry, id, parent_id} - the ancestry of the node does not extend the ancestry of its parent.

check!(repo, schema, opts \\ [])

@spec check!(Ecto.Repo.t(), module(), keyword()) :: :ok

Like check/3 but raises AncestryEx.IntegrityError.

rebuild_counter_cache(repo, schema, opts \\ [])

@spec rebuild_counter_cache(Ecto.Repo.t(), module(), keyword()) :: :ok

Recomputes the counter cache column by counting the children of every node. Does nothing unless the schema uses :counter_cache.

rebuild_depth_cache(repo, schema, opts \\ [])

@spec rebuild_depth_cache(Ecto.Repo.t(), module(), keyword()) :: :ok

Recomputes the depth cache column from the ancestry column. Does nothing unless the schema uses cache_depth: true.

restore(repo, schema, opts \\ [])

@spec restore(Ecto.Repo.t(), module(), keyword()) :: {:ok, non_neg_integer()}

Repairs the ancestry column of schema.

Every node is attached to the nearest of its ancestors that still exists (or becomes a root), cycles are broken by turning a node of the cycle into a root, and the ancestry of every node is rebuilt from those parents. Depth and counter caches are rebuilt as well.

Returns {:ok, number_of_updated_nodes}.