Lazarus (lazarus v1.0.0)

Copy Markdown View Source

Public entrypoint for Lazarus.

Most applications interact with Lazarus in two layers:

  • use Lazarus in a Repo module to inject Repo.soft_delete*, Repo.hard_delete*, read-side filtering, and update behavior that skips soft-deleted rows
  • call the functions in this module when you want the same soft-delete behavior while passing the Repo module explicitly

This module is the repo-explicit API for Lazarus's soft-delete helpers. Updates use Ecto Repo functions; use Lazarus makes Repo.update*, loaded Repo.insert_or_update*, and Repo.update_all/3 skip soft-deleted rows by default. Lazarus.Repo documents the functions injected into a Repo by use Lazarus.

Basic usage

defmodule MyApp.Repo do
  use Ecto.Repo, otp_app: :my_app, adapter: Ecto.Adapters.Postgres
  use Lazarus
end

Use Lazarus.Schema in schemas that should support soft deletion:

defmodule MyApp.Post do
  use Ecto.Schema
  use Lazarus.Schema

  schema "posts" do
    soft_deletes()
  end
end

Use Lazarus.Migrations to add the matching columns:

defmodule MyApp.Repo.Migrations.AddSoftDeletesToPosts do
  use Ecto.Migration
  use Lazarus.Migrations

  def change do
    alter table(:posts) do
      soft_deletes()
    end
  end
end
  • Lazarus.Repo - injected Repo functions and use Lazarus integration
  • Lazarus.Schema - schema helpers and schema-level cascade metadata
  • Lazarus.Migrations - migration helpers for adding matching columns
  • the HexDocs guides - workflow-oriented explanations of queries, cascading, and association replacement

Summary

Query helpers

Recursively applies Lazarus's read-side soft-delete filtering to an Ecto query.

Soft delete API

Soft-deletes a single struct or changeset with an explicit Repo module.

Soft-deletes a single struct or changeset and raises on failure.

Soft-deletes all rows matched by a schema-aware queryable.

Query helpers

deep_filter_soft_deleted(query, opts)

Recursively applies Lazarus's read-side soft-delete filtering to an Ecto query.

This is the same query-rewriting step injected into Repos that use Lazarus for reads. Repo.update_all/3 uses the same recursive filtering internally.

Returns

  • a rewritten query when filtering is applied
  • the original query unchanged when with_deleted: true is passed and the query shape is safe or explicitly allowed
  • non-query values unchanged

Options

  • :with_deleted - boolean controlling whether soft-delete filtering is bypassed for the whole query (accepted values: true, false; default: false)
  • :allow_raw_sql - boolean controlling whether raw SQL fragments are allowed (accepted values: true, false; default: false)
  • :allow_schema_less_sources - boolean controlling whether schema-less string table sources are allowed (accepted values: true, false; default: false)

Raises

  • ArgumentError when the query contains raw SQL fragments unless allow_raw_sql: true is passed
  • ArgumentError when the query contains schema-less root or join sources unless allow_schema_less_sources: true is passed

Examples

query =
  from(post in Post, select: post.id)

Lazarus.deep_filter_soft_deleted(query, [])
Lazarus.deep_filter_soft_deleted(query, with_deleted: true)
Lazarus.deep_filter_soft_deleted(query, allow_raw_sql: true)
Lazarus.deep_filter_soft_deleted(query, allow_schema_less_sources: true)

See the "Query Support" guide for the precise root, join, subquery, and CTE behaviors.

Soft delete API

soft_delete(repo, struct_or_changeset, opts \\ [])

Soft-deletes a single struct or changeset with an explicit Repo module.

This is the repo-explicit equivalent of Repo.soft_delete/2.

Returns

  • {:ok, struct} on success
  • {:error, :not_found} when the row was already soft-deleted or no longer exists

Options

  • :reason - string reason persisted to deletion_reason when the schema includes that field, or nil (default: nil)
  • :cascade - boolean controlling whether eligible associations are cascaded (accepted values: true, false; default: false)
  • :reload_after_delete - boolean controlling whether Lazarus reloads the soft-deleted struct from the database after deletion (accepted values: true, false; default: repo config :reload_after_delete, then false). The default in-memory return avoids an additional database call and is faster, but may be less accurate than a fresh load; associations on the returned struct are reset to Ecto.Association.NotLoaded. Loading after deletion is more accurate, but costs one additional database call.
  • :skip_associations - list of association field names to skip during cascading, such as [:comments, :ratings]
  • :cascade_depth - positive integer recursion cap for cascading (default: 10)

Examples

{:ok, deleted_post} = Lazarus.soft_delete(MyApp.Repo, post)
{:ok, deleted_post} = Lazarus.soft_delete(MyApp.Repo, post, reason: "Deleted by user")
{:ok, deleted_post} = Lazarus.soft_delete(MyApp.Repo, post, reload_after_delete: true)

soft_delete!(repo, struct_or_changeset, opts \\ [])

Soft-deletes a single struct or changeset and raises on failure.

This is the repo-explicit equivalent of Repo.soft_delete!/2.

Returns

  • the deleted struct on success

Options

Accepts the same options as soft_delete/3.

Raises

  • ArgumentError when the row was already soft-deleted or no longer exists

Examples

deleted_post = Lazarus.soft_delete!(MyApp.Repo, post)
deleted_post = Lazarus.soft_delete!(MyApp.Repo, post, reason: "Moderator action")
deleted_post = Lazarus.soft_delete!(MyApp.Repo, post, reload_after_delete: true)

soft_delete_all(repo, queryable, opts \\ [])

Soft-deletes all rows matched by a schema-aware queryable.

This is the repo-explicit equivalent of Repo.soft_delete_all/2.

Returns

  • {count, nil} when the query does not include a select
  • {count, returned} when the query includes a select, matching the usual Ecto bulk-update return shape

Options

  • :reason - string reason persisted to deletion_reason when the schema includes that field, or nil (default: nil)
  • :cascade - boolean controlling whether eligible associations are cascaded (accepted values: true, false; default: false)
  • :skip_associations - list of association field names to skip during cascading, such as [:comments, :ratings]
  • :cascade_depth - positive integer recursion cap for cascading (default: 10)
  • :allow_raw_sql - boolean controlling whether fragments are allowed in the bulk soft-delete query (accepted values: true, false; default: false)
  • :allow_schema_less_sources - boolean controlling whether schema-less joins and nested sources are allowed in the bulk soft-delete query (accepted values: true, false; default: false). The root source must still be schema-aware.

Query Requirements

  • the query root must be schema-aware, such as Post or {"posts", Post}
  • schema-less roots such as from(row in "posts") are not supported
  • subquery roots such as from(row in subquery(...)) are not supported
  • schema-less joins and nested schema-less sources require allow_schema_less_sources: true

See the "Query Support" guide for the full set of supported query shapes.

Examples

Lazarus.soft_delete_all(MyApp.Repo, Post)

Lazarus.soft_delete_all(
  MyApp.Repo,
  from(post in Post, where: post.author_id == ^author_id),
  reason: "Bulk cleanup"
)