Beancount.Queries (beancount_ex v0.6.0)

Copy Markdown View Source

Ecto.Query-based queries against stored Beancount directives.

These functions read directly from the database tables populated by Beancount.Storage — no booking engine or BQL required. They return Beancount.Schemas.* rows (the storage layer), not Beancount.Directives.* structs. To work with directive structs, call Beancount.Storage.load/0 instead.

For balance reports that need inventory booking and cost-lot logic, use Beancount.Report (which dispatches BQL through the configured engine).

Prerequisites

Data must be stored before querying:

Beancount.Storage.import_file("ledger.bean")
# or
Beancount.Storage.store(directives)

Public functions

FunctionDescription
list_opens/1Open directives, optionally filtered by account prefix
list_closes/0All close directives
count_transactions_by_date/0Transaction counts grouped by date
find_transactions/1Transactions filtered by payee, narration, date range
list_prices/1Price directives for one commodity
list_balances/1Balance assertions for one account
count_by_type/0Row counts per directive table

Example: filter and aggregate

Beancount.Storage.store([
  Beancount.open(~D[2026-01-01], "Assets:Bank", ["USD"]),
  Beancount.open(~D[2026-01-01], "Assets:Cash", ["USD"]),
  Beancount.transaction(~D[2026-01-15], "*", "Employer", "Salary", [
    Beancount.posting("Assets:Bank", Decimal.new("100"), "USD"),
    Beancount.posting("Income:Salary", Decimal.new("-100"), "USD")
  ])
])

# Asset accounts only
Beancount.Queries.list_opens(prefix: "Assets")
# => [%Beancount.Schemas.Open{account: "Assets:Bank"}, ...]

# Transactions in January
Beancount.Queries.find_transactions(
  payee: "Employer",
  from_date: ~D[2026-01-01],
  to_date: ~D[2026-01-31]
)

# Ledger composition
Beancount.Queries.count_by_type()
# => [opens: 2, transactions: 1, ...]

Summary

Functions

Count all stored directives grouped by type.

Count transactions grouped by date, ordered by date.

Find transactions matching the given criteria, ordered by date.

List all balance assertions for an account, ordered by date.

List all close directives, ordered by account name.

List all open directives, optionally filtered by account prefix.

List all price directives for a commodity, ordered by date.

Functions

count_by_type()

@spec count_by_type() :: [{atom(), non_neg_integer()}]

Count all stored directives grouped by type.

Returns a keyword-style list of {type, count} tuples, one per directive table.

Includes one entry for every directive table, so undated directives (includes, plugins, pushtags, poptags, queries) are represented too.

Examples

Beancount.Queries.count_by_type()
# => [opens: 5, closes: 0, commodities: 3, transactions: 42, balances: 4,
#     prices: 12, notes: 0, documents: 0, events: 0, customs: 1, pads: 0,
#     includes: 0, options: 2, plugins: 0, push_tags: 0, pop_tags: 0,
#     queries: 0]

count_transactions_by_date()

@spec count_transactions_by_date() :: [{Date.t(), non_neg_integer()}]

Count transactions grouped by date, ordered by date.

Returns a list of {date, count} tuples.

Examples

Beancount.Queries.count_transactions_by_date()
# => [{~D[2026-01-15], 1}, {~D[2026-02-15], 3}]

find_transactions(opts \\ [])

@spec find_transactions(keyword()) :: [Beancount.Schemas.Transaction.t()]

Find transactions matching the given criteria, ordered by date.

Supported options (all optional, combined with AND):

  • :payee - exact payee match.
  • :narration - substring match on the narration via SQL LIKE. Case sensitivity is backend-dependent: the default SQLite backend is case-insensitive for ASCII only; other backends (e.g. the planned PostgreSQL storage) treat LIKE as case-sensitive.
  • :from_date - only transactions on or after this Date.
  • :to_date - only transactions on or before this Date.

Examples

# By payee
Beancount.Queries.find_transactions(payee: "Employer")

# Narration substring within a date range
Beancount.Queries.find_transactions(narration: "coffee", from_date: ~D[2026-01-01], to_date: ~D[2026-01-31])

list_balances(account)

@spec list_balances(String.t()) :: [Beancount.Schemas.Balance.t()]

List all balance assertions for an account, ordered by date.

Examples

Beancount.Queries.list_balances("Assets:Bank")
# => [%Beancount.Schemas.Balance{account: "Assets:Bank", date: ~D[2026-01-31]}]

list_closes()

@spec list_closes() :: [Beancount.Schemas.Close.t()]

List all close directives, ordered by account name.

Examples

Beancount.Queries.list_closes()
# => [%Beancount.Schemas.Close{account: "Assets:Bank", date: ~D[2026-12-31]}]

list_opens(opts \\ [])

@spec list_opens(keyword()) :: [Beancount.Schemas.Open.t()]

List all open directives, optionally filtered by account prefix.

Results are ordered by account name. Pass :prefix to restrict to accounts under a given root (matched as prefix:%).

Examples

# All opens
Beancount.Queries.list_opens()
# => [%Beancount.Schemas.Open{account: "Assets:Bank"}, ...]

# Only asset accounts
Beancount.Queries.list_opens(prefix: "Assets")
# => [%Beancount.Schemas.Open{account: "Assets:Bank"}, %Beancount.Schemas.Open{account: "Assets:Cash"}]

list_prices(commodity)

@spec list_prices(String.t()) :: [Beancount.Schemas.Price.t()]

List all price directives for a commodity, ordered by date.

Examples

Beancount.Queries.list_prices("AAPL")
# => [%Beancount.Schemas.Price{commodity: "AAPL", date: ~D[2026-01-02]}]