Skip to main content

SearchFtsDb

Struct SearchFtsDb 

Source
pub struct SearchFtsDb { /* private fields */ }

Implementations§

Source§

impl SearchFtsDb

Source

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)
Source

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).

Source

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.

Source

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.

Source

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).

Source

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.

Source

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.

Source

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.

Source

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.

Source

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, …].

Source

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).

Source

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.

Source

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.

Source

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.

Source

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).

Source

pub fn symbol_count(&self) -> usize

Number of rows in the FTS table (total indexed symbols).

Source

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.

Source

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.

Source

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.

Source

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_orig ends 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.

Source

pub fn has_data(&self) -> bool

True if the FTS table has at least one row.

Source

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.

Source

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:

  1. get_tree("/asd/v1/ledger") — full ledger tree walk
  2. get_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).

Source

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_adjustments
  • explain_feedback_impacts
  • apply_file_scope_feedback
  • apply_paths_filter
  • apply_exclusions

Cost: one SQL query per call regardless of batch size.

Source

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>.

Source

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).

Source

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).

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

pub fn upsert_effects( &self, symbol_id: &str, ref_name: &str, decl: &EffectDecl, ) -> Result<()>

Insert or replace the EffectDecl for a symbol.

Source

pub fn delete_effects_for( &self, symbol_ids: &[String], ref_name: &str, ) -> Result<()>

Drop the cached EffectDecls of symbols that left the index.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

pub fn list_all_feedback(&self) -> Result<Vec<FeedbackEntry>>

Source

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.

Source

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.

Source

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.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.