Account search by keywords/aliases (e.g. "car" → Expenses:Vehicle:Gas) #47

Open
opened 2026-06-15 18:55:30 +00:00 by padreug · 0 comments
Owner

Goal

Let webapp (and the Libra UI) find accounts by keyword/alias, not just by exact hierarchical name. Typing "car" should surface Expenses:Vehicle:Gas, "rent" → Expenses:Housing:Rent, etc. Improves account selection during expense/receivable entry, especially for non-accountant collective members who don't know the chart layout.

Mechanism is settled; storage location is the open question

A Beancount open directive can carry arbitrary metadata, so keywords can live on the account itself:

2024-01-01 open Expenses:Vehicle:Gas
  keywords: "car auto fuel petrol vehicle"

Verified constraints from the Beancount grammar:

  • A metadata value is a single scalar (STRING | account | DATE | currency | TAG | BOOL | NONE | number | amount) — no list type, and keys are unique per directive. So keywords are one delimited string (space- or comma-separated), not a list.
  • Metadata key token is [a-z][a-zA-Z0-9\-_]+, so keywords is a valid key.

Important: metadata is storage only — nothing reads it for search automatically. Fava's own account filter matches account names only. The matching behavior is ours to build regardless of where keywords are stored.

Decision: where do keywords live?

Beancount Open metadata Libra operational DB
Source of truth One place, travels with the ledger, editable in Fava, survives re-sync Second store; can drift from the account
Ledger cleanliness Mixes a UX hint into the accounting file Keeps ledger pure-accounting
Project convention CLAUDE.md designates SQLite for "operational state Fava doesn't and shouldn't own" — search aliases arguably qualify
Search ergonomics Needs sync/BQL plumbing to expose; flat string value Easy to index for fast typeahead

Leaning (not decided): keywords are intrinsically about the account and benefit from being edited once and traveling with it → Beancount metadata as source of truth, synced into Libra's account model for search. The description metadata we already write is a mild precedent. But the "keep the ledger clean / it's a UX concern" case for the Libra DB is legitimate. Pick one before building.

Implementation sketch (once storage is decided)

If Beancount metadata:

  1. Add an optional keywords field to the add-account dialog + CreateChartAccount model; write keywords: metadata in format/add_account (reuse the new _escape_beancount_string).
  2. Carry keywords through account_sync into Libra's account model / AccountWithPermissions.
  3. Expose on the accounts API; implement substring/token matching in the accounts endpoint or webapp typeahead.

If Libra DB:

  1. Add a keywords/aliases column to the account operational store + a small edit UI.
  2. Match server-side in the accounts endpoint; expose to webapp.

Out of scope / dependencies

  • Builds on the add-account UI (PR #46) and reuses _escape_beancount_string from that PR if the metadata route is chosen.
  • webapp consumes the resulting accounts API; the webapp typeahead UX is a separate piece of work there.
## Goal Let webapp (and the Libra UI) find accounts by **keyword/alias**, not just by exact hierarchical name. Typing "car" should surface `Expenses:Vehicle:Gas`, "rent" → `Expenses:Housing:Rent`, etc. Improves account selection during expense/receivable entry, especially for non-accountant collective members who don't know the chart layout. ## Mechanism is settled; storage location is the open question A Beancount `open` directive can carry arbitrary metadata, so keywords *can* live on the account itself: ``` 2024-01-01 open Expenses:Vehicle:Gas keywords: "car auto fuel petrol vehicle" ``` Verified constraints from the Beancount grammar: - A metadata **value** is a single scalar (`STRING | account | DATE | currency | TAG | BOOL | NONE | number | amount`) — **no list type**, and keys are unique per directive. So keywords are one delimited string (space- or comma-separated), not a list. - Metadata key token is `[a-z][a-zA-Z0-9\-_]+`, so `keywords` is a valid key. **Important:** metadata is *storage only* — nothing reads it for search automatically. Fava's own account filter matches account **names** only. The matching behavior is ours to build regardless of where keywords are stored. ### Decision: where do keywords live? | | Beancount Open metadata | Libra operational DB | |---|---|---| | Source of truth | One place, travels with the ledger, editable in Fava, survives re-sync | Second store; can drift from the account | | Ledger cleanliness | Mixes a UX hint into the accounting file | Keeps ledger pure-accounting | | Project convention | — | CLAUDE.md designates SQLite for "operational state Fava doesn't and shouldn't own" — search aliases arguably qualify | | Search ergonomics | Needs sync/BQL plumbing to expose; flat string value | Easy to index for fast typeahead | **Leaning** (not decided): keywords are intrinsically *about* the account and benefit from being edited once and traveling with it → Beancount metadata as source of truth, synced into Libra's account model for search. The `description` metadata we already write is a mild precedent. But the "keep the ledger clean / it's a UX concern" case for the Libra DB is legitimate. **Pick one before building.** ## Implementation sketch (once storage is decided) If **Beancount metadata**: 1. Add an optional `keywords` field to the add-account dialog + `CreateChartAccount` model; write `keywords:` metadata in `format`/`add_account` (reuse the new `_escape_beancount_string`). 2. Carry `keywords` through `account_sync` into Libra's account model / `AccountWithPermissions`. 3. Expose on the accounts API; implement substring/token matching in the accounts endpoint or webapp typeahead. If **Libra DB**: 1. Add a `keywords`/`aliases` column to the account operational store + a small edit UI. 2. Match server-side in the accounts endpoint; expose to webapp. ## Out of scope / dependencies - Builds on the add-account UI (PR #46) and reuses `_escape_beancount_string` from that PR if the metadata route is chosen. - webapp consumes the resulting accounts API; the webapp typeahead UX is a separate piece of work there.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
aiolabs/libra#47
No description provided.