Skip to content

Triggers and Views

Tom Laird-McConnell edited this page May 24, 2026 · 3 revisions

Triggers and Materialized Views

Triggers via On

Triggers run inline after each save or delete. They receive the object, the trigger kind (Saved or Deleted), and the full DB instance.

var db = await catalog.GetDatabaseAsync("mydb", config =>
{
    config.Store<Note>();
    config.On<Note>(async (note, kind, db) =>
    {
        Console.WriteLine($"Note {note.NoteId} was {kind}");
    });
});

Materialized views

Use On triggers to build derived objects that stay in sync automatically. Whenever the trigger runs you can create/update/delete other objects.

public class NoteView
{
    [Key]
    public string Id { get; set; } = "";

    [Queryable(QueryableMode.NotAnalyzed)]
    public string NoteId { get; set; } = "";

    [Queryable]
    public string AuthorDisplay { get; set; } = "";

    [Queryable]
    public string Content { get; set; } = "";
}

var db = await catalog.GetDatabaseAsync("mydb", config =>
{
    config.Store<Note>();
    config.Store<NoteView>();
    config.On<Note>(async (note, kind, db) =>
    {
        if (kind == TriggerKind.Deleted)
        {
            await db.DeleteManyAsync<NoteView>(nv => nv.NoteId == note.NoteId);
            return;
        }

        var actor = await db.GetAsync<Actor>(note.AuthorId);
        await db.SaveAsync(new NoteView
        {
            Id = $"nv-{note.NoteId}",
            NoteId = note.NoteId,
            AuthorDisplay = actor?.DisplayName ?? "",
            Content = note.Content,
        });
    });
});

Handlers can trigger further handlers (cascading views). Cycle detection prevents infinite loops -- if the same type appears twice in a handler chain, processing stops.

Built-in handlers

LottaDB registers built-in On handlers for:

  • Lucene indexing -- every save/delete updates the Lucene index automatically
  • JsonSchema -- when a JsonSchema entity is saved, the dynamic mapper is updated and documents are re-indexed if the schema changed. When deleted, the mapper and Lucene entries are removed.

Error handling

Handler errors never block the source save/delete. They are captured in ObjectResult.Errors:

var result = await db.SaveAsync(note);
if (result.Errors.Count > 0)
{
    // log handler failures
}

Clone this wiki locally