pub struct SearchFtsDb { /* private fields */ }Implementations§
Source§impl SearchFtsDb
impl SearchFtsDb
Sourcepub fn open(db_path: &Path) -> Result<Self>
pub fn open(db_path: &Path) -> Result<Self>
Open (or create) the FTS index in db_path.
Pragmas are tuned adaptively based on DB file size. Users can override
the defaults by adding a [performance] section to .asd/config.toml
in the project root (the directory that contains .asd-state.db):
[performance]
cache_size_kb = 32768 # override adaptive cache (default: ~80% of DB, 8–64 MB)
mmap_size_mb = 512 # override mmap window (default: 256 MB)Sourcepub fn rebuild(&self, symbols: &[Symbol]) -> Result<()>
pub fn rebuild(&self, symbols: &[Symbol]) -> Result<()>
Atomically replace the entire FTS table from symbols.
This is the canonical indexing path. Because asd index already has the
complete current-world snapshot, a full rebuild is cheaper than tracking
which files changed and avoids stale rows from deleted files.
ledger_data: symbol_id → (ledger_text, ledger_flags) map built at
index time from the ledger tree. Pass &HashMap::new() when no ledger
data is available (e.g. test helpers that call rebuild directly).
Sourcepub fn rebuild_refs(
&self,
symbols: &[&Symbol],
ledger_data: &HashMap<String, (String, String)>,
) -> Result<()>
pub fn rebuild_refs( &self, symbols: &[&Symbol], ledger_data: &HashMap<String, (String, String)>, ) -> Result<()>
Like [rebuild] but accepts a slice of references — avoids a copy when
the caller already has a deduplicated Vec<&Symbol>.
ledger_data: symbol_id → (ledger_text, ledger_flags). Pass
&HashMap::new() when ledger data is unavailable.
Sourcepub fn last_indexed_at(&self) -> Option<i64>
pub fn last_indexed_at(&self) -> Option<i64>
Unix timestamp (seconds) of the last rebuild() call, or None if
the index has never been built.
Sourcepub fn fts_last_rebuild_ok(&self) -> Option<bool>
pub fn fts_last_rebuild_ok(&self) -> Option<bool>
Whether the FTS rebuild succeeded during the last asd index run.
Returns None if no mark_symbols_indexed call has been recorded yet
(pre-0.9.70 index runs or DBs that have never been indexed with this version).
Sourcepub fn mark_symbols_indexed(&self, fts_succeeded: bool) -> Result<()>
pub fn mark_symbols_indexed(&self, fts_succeeded: bool) -> Result<()>
Record the outcome of a symbol-indexing run. Call this from
index_pipeline after the FTS rebuild attempt (success or failure).
Writes symbols_indexed_at and fts_last_rebuild_ok into
asd_index_meta. This is the authoritative signal used by
stale_warning() to detect the “symbols fresh / FTS stale” state.
Sourcepub fn fts_symbol_row_count(&self) -> i64
pub fn fts_symbol_row_count(&self) -> i64
Number of rows in the asd_search_fts symbol search table. Used by
Engine::warm_caches to decide whether a full-text rebuild is needed:
a populated FTS table is never clobbered (it carries index-time
ledger_text/ledger_flags denormalizations), only an empty one is
rebuilt.
Sourcepub fn symbols_cached_for(&self, ref_name: &str) -> bool
pub fn symbols_cached_for(&self, ref_name: &str) -> bool
Returns true when asd_symbols_cache has at least one row for
ref_name. Used as the primary guard before attempting a cached read —
if the cache is empty the caller falls back to the git path.
Sourcepub fn sync_symbols(&self, symbols: &[&Symbol], ref_name: &str) -> Result<()>
pub fn sync_symbols(&self, symbols: &[&Symbol], ref_name: &str) -> Result<()>
Full replace of asd_symbols_cache for ref_name. Called after every
asd index run so the cache always reflects the current snapshot.
Sourcepub fn sync_call_edges(
&self,
callees_of: &HashMap<String, Vec<String>>,
callers_of: &HashMap<String, Vec<String>>,
ref_name: &str,
) -> Result<()>
pub fn sync_call_edges( &self, callees_of: &HashMap<String, Vec<String>>, callers_of: &HashMap<String, Vec<String>>, ref_name: &str, ) -> Result<()>
Full replace of asd_call_edges for ref_name. callees_of maps
caller_id → [callee_id, …]; callers_of maps callee_id → [caller_id, …].
Sourcepub fn build_id_map_cached(&self, ref_name: &str) -> HashMap<String, Symbol>
pub fn build_id_map_cached(&self, ref_name: &str) -> HashMap<String, Symbol>
Build the full symbol_id → Symbol map from asd_symbols_cache.
Returns an empty map on any error (caller falls back to git).
Sourcepub fn files_for_qnames(
&self,
qnames: &[&str],
_ref_name: &str,
) -> HashMap<String, String>
pub fn files_for_qnames( &self, qnames: &[&str], _ref_name: &str, ) -> HashMap<String, String>
Plan E t-005: bulk qname → file lookup. Returns a HashMap with one entry per qname found in the symbol cache (qnames not in the cache are simply absent from the result). Callers use this to avoid per-candidate SQL roundtrips when they only need the file path, not the full Symbol JSON.
Note: asd_symbols_meta is single-ref by design (rebuilt fresh per
asd index .), so the ref_name parameter is currently unused
but reserved for the future multi-ref world.
Sourcepub fn get_symbol_by_qname_cached(
&self,
qname: &str,
ref_name: &str,
) -> Option<Symbol>
pub fn get_symbol_by_qname_cached( &self, qname: &str, ref_name: &str, ) -> Option<Symbol>
Look up a single Symbol by qname from the cache.
Sourcepub fn get_neighbors_cached(
&self,
symbol_id: &str,
direction: &str,
ref_name: &str,
) -> Vec<String>
pub fn get_neighbors_cached( &self, symbol_id: &str, direction: &str, ref_name: &str, ) -> Vec<String>
Return the neighbor IDs for symbol_id in the given direction
("caller" or "callee"). Empty Vec if none or on error.
Sourcepub fn build_edge_maps_cached(
&self,
ref_name: &str,
) -> (HashMap<String, Vec<String>>, HashMap<String, Vec<String>>)
pub fn build_edge_maps_cached( &self, ref_name: &str, ) -> (HashMap<String, Vec<String>>, HashMap<String, Vec<String>>)
Build both full edge maps from asd_call_edges in one query:
(callers_of, callees_of), each symbol_id → [neighbor_id, …].
The bulk analog of Self::get_neighbors_cached — callers that walk
many nodes (graph BFS) use this instead of one query per node.
Returns empty maps on any error (caller falls back to git).
Sourcepub fn symbol_count(&self) -> usize
pub fn symbol_count(&self) -> usize
Number of rows in the FTS table (total indexed symbols).
Sourcepub fn annotated_symbol_count(&self, ref_name: &str) -> usize
pub fn annotated_symbol_count(&self, ref_name: &str) -> usize
Count symbols that have any ledger entries.
Field-test refinement (AcmeFlow feedback, 2026-06-04): this used
to read SELECT COUNT(*) FROM asd_search_fts WHERE ledger_text != '',
but ledger_text is only populated at full asd index time. Any
entries written via asd think, asd ledger append, or
asd annotate-commit go to asd_ledger_cache via upsert_ledger_entry
— they’re invisible to the FTS table until the next reindex. The
result: trust scoring kept reporting unannotated and projects
stuck in a confidence-erasing loop (“write entries → ASD says
nothing’s annotated → reindex → maybe ASD sees them”). Reading
from the live cache instead means writes flip the count instantly.
ref_name is required for accurate counting — asd_ledger_cache
stores entries per-ref. The DISTINCT collapses multiple entries
on the same symbol so we count annotated symbols, not entries.
Sourcepub fn search(
&self,
query: &str,
filters: &FtsFilters,
limit: usize,
) -> Result<Vec<FtsHit>>
pub fn search( &self, query: &str, filters: &FtsFilters, limit: usize, ) -> Result<Vec<FtsHit>>
Ranked FTS search. Returns hits ordered by relevance (highest first).
BM25 column weights: qname=10, signature=5, doc=5, file=2.
Tests excluded unless filters.include_tests is true.
Sourcepub fn count_distinct_files_per_token(
&self,
tokens: &[&str],
include_tests: bool,
) -> Result<Vec<(String, usize)>>
pub fn count_distinct_files_per_token( &self, tokens: &[&str], include_tests: bool, ) -> Result<Vec<(String, usize)>>
For each token in tokens, return (token, distinct_file_count) using a single
SQL UNION ALL query instead of N separate searches. Used by
detect_ambiguous_tokens to batch all token checks into one round-trip.
Sourcepub fn has_exact_symbol_name(&self, name: &str) -> bool
pub fn has_exact_symbol_name(&self, name: &str) -> bool
Returns true if name exactly matches any symbol’s short name or qname.
Matches:
qname_origends with.{name}or equals{name}(case-insensitive)
Used by the uncertainty override guard to distinguish “exact symbol lookup”
from generic broad queries, preventing false-high uncertainty on direct
symbol names like AcmeFlowViewModel.
Sourcepub fn get_symbols_meta_bulk(
&self,
qnames: &[&str],
) -> HashMap<String, SymbolMeta>
pub fn get_symbols_meta_bulk( &self, qnames: &[&str], ) -> HashMap<String, SymbolMeta>
Bulk-resolve qnames to SymbolMeta from the asd_symbols_meta table.
Returns a map of qname → SymbolMeta for all qnames that have a row.
Missing entries are silently omitted; callers should fall back to a git
object read when the result map does not contain a needed qname.
Cost: one SQL query regardless of how many qnames are requested.
Sourcepub fn anchor_candidates(
&self,
tokens: &[String],
existing_qnames: &HashSet<String>,
limit: usize,
) -> Vec<(String, String)>
pub fn anchor_candidates( &self, tokens: &[String], existing_qnames: &HashSet<String>, limit: usize, ) -> Vec<(String, String)>
Anchor-candidate lookup: return (qname, symbol_id) pairs for symbols
that have Invariant or Hazard ledger entries whose text matches at least
one of tokens, and are not already in existing_qnames.
Replaces the two get_tree calls in ledger_anchor_pass:
get_tree("/asd/v1/ledger")— full ledger tree walkget_tree("/asd/v1/index/by-qname")— reverse id→qname map
Both replaced by a single SQLite scan against the UNINDEXED
ledger_text / ledger_flags columns populated at asd index time.
Returns at most limit results (matches MAX_ANCHORS in candidates.rs).
Sourcepub fn resolve_qnames_bulk(
&self,
qnames: &[&str],
) -> HashMap<String, ResolvedSymbol>
pub fn resolve_qnames_bulk( &self, qnames: &[&str], ) -> HashMap<String, ResolvedSymbol>
Bulk-resolve qnames to ResolvedSymbol from the FTS table.
Returns HashMap<qname, ResolvedSymbol> for all qnames that have a row.
Missing entries are silently omitted (unknown qname or stale index).
Replaces per-symbol get_symbol_by_qname git reads in:
apply_feedback_adjustmentsexplain_feedback_impactsapply_file_scope_feedbackapply_paths_filterapply_exclusions
Cost: one SQL query per call regardless of batch size.
Sourcepub fn resolve_symbol_ids_bulk(
&self,
symbol_ids: &[&str],
) -> HashMap<String, ResolvedSymbol>
pub fn resolve_symbol_ids_bulk( &self, symbol_ids: &[&str], ) -> HashMap<String, ResolvedSymbol>
Also resolve by symbol_id batch — used by feedback functions that have symbol_ids (not qnames) and need file/qname/kind for sibling suppression.
Cost: one SQL query, returns HashMap<symbol_id, ResolvedSymbol>.
Sourcepub fn symbols_with_constraint_penalties(
&self,
ref_name: &str,
) -> Result<Vec<String>>
pub fn symbols_with_constraint_penalties( &self, ref_name: &str, ) -> Result<Vec<String>>
Plan E t-004: one-shot SQL scan that returns every symbol_id
whose ledger contains a Constraint or Decision entry carrying a
penalty role (stale-api / audit-pending). Used by
apply_constraint_penalties to avoid an N-walk over the ledger
per query.
Returns an empty vec when the cache is empty (caller falls back to the per-candidate ledger walk).
Sourcepub fn symbols_with_constraint_penalties_scoped(
&self,
ref_name: &str,
) -> Result<Vec<(String, Option<Vec<String>>)>>
pub fn symbols_with_constraint_penalties_scoped( &self, ref_name: &str, ) -> Result<Vec<(String, Option<Vec<String>>)>>
Plan E t-008: like symbols_with_constraint_penalties but also
returns the optional scope-glob list parsed from each entry’s
body JSON. Schema: body MAY be a JSON object containing
{"scope": ["glob1", "glob2", ...]}. When present, the penalty
applies only to symbols whose file matches at least one glob;
when absent or unparseable, the penalty applies globally
(preserves the Plan E t-004 contract).
The same symbol_id may appear multiple times in the result when it has multiple penalty entries (e.g. one global + one scoped). The caller suppresses on ANY match (most permissive scope wins).
Sourcepub fn ledger_entry_count_for(&self, symbol_id: &str, ref_name: &str) -> usize
pub fn ledger_entry_count_for(&self, symbol_id: &str, ref_name: &str) -> usize
Number of ledger rows cached for this (symbol_id, ref_name) pair. Returns 0 when the symbol hasn’t been cached yet — triggers git fallback.
Sourcepub fn upsert_ledger_entry(
&self,
entry: &LedgerEntry,
ref_name: &str,
) -> Result<()>
pub fn upsert_ledger_entry( &self, entry: &LedgerEntry, ref_name: &str, ) -> Result<()>
Insert or replace a single ledger entry for ref_name.
Field-test refinement (AcmeFlow, 2026-06-04): also backfills
the symbol’s row in asd_search_fts so the ledger_text /
ledger_flags columns stay warm between reindexes. Previously these
columns were ONLY populated at full asd index time, which meant
any entries written via asd think/asd ledger append were
invisible to search-time ledger ranking until the next reindex.
The backfill is best-effort: if the symbol row doesn’t exist in
FTS yet (rare — symbol writes happen before any ledger writes),
the UPDATE silently no-ops and the next reindex catches up.
Sourcepub fn list_ledger_entries_for(
&self,
symbol_id: &str,
ref_name: &str,
) -> Result<Vec<LedgerEntry>>
pub fn list_ledger_entries_for( &self, symbol_id: &str, ref_name: &str, ) -> Result<Vec<LedgerEntry>>
Return all ledger entries for a symbol (including superseded), newest first.
The caller’s list_entries default method handles supersede filtering.
Sourcepub fn ledger_cache_entry_ids(&self, ref_name: &str) -> Result<Vec<String>>
pub fn ledger_cache_entry_ids(&self, ref_name: &str) -> Result<Vec<String>>
Every cached ledger entry id for ref_name, without parsing bodies —
the cheap side of the ledger integrity count.
Sourcepub fn all_ledger_entries(
&self,
ref_name: &str,
) -> Result<(Vec<LedgerEntry>, Vec<String>)>
pub fn all_ledger_entries( &self, ref_name: &str, ) -> Result<(Vec<LedgerEntry>, Vec<String>)>
Every cached ledger entry for ref_name. Rows whose body no longer
parses come back as ids in the second vec rather than being skipped,
so an integrity count cannot under-report what the cache holds.
Sourcepub fn sync_ledger_entries(
&self,
entries: &[(String, LedgerEntry)],
ref_name: &str,
) -> Result<()>
pub fn sync_ledger_entries( &self, entries: &[(String, LedgerEntry)], ref_name: &str, ) -> Result<()>
Bulk-insert ledger entries in a single transaction — used by asd index
to reconcile the SQLite cache from the authoritative git store.
entries: slice of (symbol_id, LedgerEntry) pairs.
Sourcepub fn effects_cached_for(&self, symbol_id: &str, ref_name: &str) -> bool
pub fn effects_cached_for(&self, symbol_id: &str, ref_name: &str) -> bool
True when an EffectDecl for this (symbol_id, ref_name) is cached.
Sourcepub fn upsert_effects(
&self,
symbol_id: &str,
ref_name: &str,
decl: &EffectDecl,
) -> Result<()>
pub fn upsert_effects( &self, symbol_id: &str, ref_name: &str, decl: &EffectDecl, ) -> Result<()>
Insert or replace the EffectDecl for a symbol.
Sourcepub fn delete_effects_for(
&self,
symbol_ids: &[String],
ref_name: &str,
) -> Result<()>
pub fn delete_effects_for( &self, symbol_ids: &[String], ref_name: &str, ) -> Result<()>
Drop the cached EffectDecls of symbols that left the index.
Sourcepub fn get_effects_for(
&self,
symbol_id: &str,
ref_name: &str,
) -> Result<Option<EffectDecl>>
pub fn get_effects_for( &self, symbol_id: &str, ref_name: &str, ) -> Result<Option<EffectDecl>>
Return the cached EffectDecl for a symbol, or None if not cached.
Sourcepub fn sync_effects(
&self,
entries: &[(String, EffectDecl)],
ref_name: &str,
) -> Result<()>
pub fn sync_effects( &self, entries: &[(String, EffectDecl)], ref_name: &str, ) -> Result<()>
Bulk-insert effects in a single transaction.
entries: slice of (symbol_id, EffectDecl) pairs.
Sourcepub fn feedback_count(&self) -> usize
pub fn feedback_count(&self) -> usize
Number of feedback rows currently cached in SQLite. Used as a guard: if 0, the caller should fall back to git.
Sourcepub fn upsert_feedback(&self, e: &FeedbackEntry) -> Result<()>
pub fn upsert_feedback(&self, e: &FeedbackEntry) -> Result<()>
Insert or replace a single feedback entry.
created_at is stored as an RFC 3339 string so it sorts correctly
in text order and round-trips through list_all_feedback accurately.
Sourcepub fn delete_feedback(&self, entry_id: &str) -> Result<usize>
pub fn delete_feedback(&self, entry_id: &str) -> Result<usize>
Return all feedback entries from the SQLite cache, newest first. Hard-delete one feedback row from the cache. Returns rows removed, so a caller can tell “gone” from “was never here” — a purge that reports success without touching anything is how the two stores drift apart.
Only for asd feedback purge. Retiring a verdict normally goes through
upsert_feedback via record, which keeps the row and marks it.
pub fn list_all_feedback(&self) -> Result<Vec<FeedbackEntry>>
Sourcepub fn sync_feedback_entries(&self, entries: &[FeedbackEntry]) -> Result<()>
pub fn sync_feedback_entries(&self, entries: &[FeedbackEntry]) -> Result<()>
Bulk-insert feedback entries from an authoritative slice (e.g. from the
git store after asd index). Uses INSERT OR REPLACE so existing rows
are overwritten. Wraps all inserts in a single transaction for speed.
Sourcepub fn file_stem_candidates(
&self,
token: &str,
filters: &FtsFilters,
limit: usize,
) -> Result<Vec<FtsHit>>
pub fn file_stem_candidates( &self, token: &str, filters: &FtsFilters, limit: usize, ) -> Result<Vec<FtsHit>>
Secondary file-stem scan: return one representative symbol per file
whose stored path contains token (case-insensitive substring match).
Used to inject view/render files that BM25 misses when the query term
appears only in the file name and not in any indexed symbol text.
Returns at most limit (symbol_id, qname, file) triples.
Sourcepub fn file_stem_candidates_batch(
&self,
tokens: &[String],
filters: &FtsFilters,
limit: usize,
) -> Result<Vec<FtsHit>>
pub fn file_stem_candidates_batch( &self, tokens: &[String], filters: &FtsFilters, limit: usize, ) -> Result<Vec<FtsHit>>
Batch version of [file_stem_candidates]: query all tokens in a
single SQL UNION ALL instead of one round-trip per token.
Returns one representative symbol per distinct file (lowest line number),
deduplicated across token arms. Results are capped at limit.
Falls back to an empty vec if tokens is empty or all tokens are
shorter than 2 characters.