Documentation
¶
Overview ¶
Package dbctx compiles a PostgreSQL database into a compact, queryable context index for text-to-SQL systems, AI agents, and database-aware applications.
dbctx connects to PostgreSQL, extracts schema metadata, analyzes field statistics from pg_stats, discovers JSONB structure via sampling, and builds a full-text search index — all deterministic, no generative LLM or external service required. The result is a Index that answers natural-language queries about which tables, columns, values, and relationships are relevant to a given question.
Two optional layers can augment that retrieval, both off by default in terms of resource cost but the first on by default in terms of behavior:
- Semantic retrieval: a small local embedding model (on by default — see Options.NoSemantic) recovers paraphrases lexical matching structurally can't (e.g. "buyers" finding a customers table), fused with, never replacing, the deterministic signals. See Index.Query and the package README's "Semantic retrieval" section.
- Terminology: a user-controlled dictionary mapping domain abbreviations/jargon to exact schema objects, populated only via explicit review — see Index.TerminologyPrompt and Index.ImportTerminology.
The index can be stored on disk as a portable .dtx file (SQLite) or kept entirely in memory for ephemeral use. It is safe for concurrent access from multiple goroutines.
Quick start ¶
Build an in-memory index and query it:
idx, err := dbctx.Build(ctx, "postgres://localhost/mydb", nil)
if err != nil {
log.Fatal(err)
}
defer idx.Close()
result, err := idx.Query("failed reviews last month")
if err != nil {
log.Fatal(err)
}
fmt.Println(result.Matched().Text())
ResultSet.Matched returns every table relevant to answering the query — direct hits plus FK-expanded join context, not just tables that scored on their own. The Selection.Text output is a compact, notation-annotated schema ready to pass to an LLM or text-to-SQL system:
--- notation ---
PK: primary key col → table foreign key
...
reviews (score: 15.24)
PK: id
org_id → orgs
status character varying(50) [state]
{completed, failed, created, in_progress}
metadata jsonb
$.provider string {github, gitlab}
Persisting the index ¶
Save the index to a .dtx file for later reuse — no PostgreSQL needed:
idx, err := dbctx.Build(ctx, dsn, &dbctx.Options{Path: "mydb.dtx"})
// ...later...
idx, err = dbctx.Open("mydb.dtx")
Non-blocking startup ¶
For applications that need the index available without blocking startup, use BuildAsync. Queries made before the build completes will block automatically:
idx, ready, err := dbctx.BuildAsync(ctx, dsn, nil)
if err != nil {
log.Fatal(err)
}
defer idx.Close()
// Register idx with your application immediately...
<-ready // or: <-idx.Ready()
Selection API ¶
Query results can be filtered and rendered in several ways:
result, _ := idx.Query("failed reviews")
result.Matched().Text() // matched + FK-expanded, with legend
result.Matched().TextRaw() // same, no legend
result.ScoredOnly().Text() // only tables that scored directly
result.Include("reviews", "orgs").Text() // specific tables
result.Matched().Exclude("migrations").Text() // matched minus exclusions
Semantic retrieval ¶
By default, Build also builds a local embedding-based semantic index (downloading the model to a local cache on first use) and Index.Query fuses it with lexical/fuzzy matching automatically. Set Options.NoSemantic to skip it:
idx, err := dbctx.Build(ctx, dsn, &dbctx.Options{NoSemantic: true})
Index.Query's result exposes why a semantic-only match appeared via ResultSet.SemanticHits — the evidence is never a black box.
Terminology ¶
Terminology maps domain vocabulary (abbreviations, acronyms, jargon) to exact schema objects, as a third retrieval signal fully independent of both lexical and semantic matching. dbctx never populates it automatically — Index.TerminologyPrompt generates a self-contained prompt for you to run through an LLM of your choice, and Index.ImportTerminology (or Index.ImportTerminologyFile, Index.ImportTerminologyGroups) validates and loads the reviewed result back in:
prompt, _ := idx.TerminologyPrompt() // ...paste prompt into an LLM, review its output... result, _ := idx.ImportTerminology(llmOutputJSON)
Schema fingerprint ¶
A .dtx file is a snapshot: nothing re-checks it against the live database on its own, so an application holding onto a stale one after a schema change answers questions against a schema that no longer exists. Index.SchemaFingerprint returns the fingerprint Build/BuildAsync computed and stored automatically (table/column shape only — schema, table, column, data type, nullable; constraints/indexes/row-estimate churn deliberately excluded), and LiveFingerprint recomputes one fresh against a live DSN for comparison, without opening or writing a .dtx:
stored, _ := idx.SchemaFingerprint()
live, _ := dbctx.LiveFingerprint(ctx, dsn, nil)
if stored != live {
// stale relative to the real schema — rebuild before trusting it
}
Use cases ¶
dbctx is designed for any system that needs to understand a PostgreSQL database at query time: text-to-SQL generation, natural-language analytics, AI agents, database explorers, BI tools, and developer assistants. It replaces repeated full-schema dumps with a deterministic, queryable index.
Example ¶
This example demonstrates building an in-memory index from PostgreSQL and querying it with natural language. The output is a compact, notation-annotated schema ready for an LLM or text-to-SQL system.
package main
import (
"context"
"fmt"
"log"
"github.com/shrsv/dbctx"
)
func main() {
ctx := context.Background()
idx, err := dbctx.Build(ctx, "postgres://localhost/mydb", nil)
if err != nil {
log.Fatal(err)
}
defer idx.Close()
result, err := idx.Query("failed reviews last month")
if err != nil {
log.Fatal(err)
}
// Matched() returns tables that scored a direct hit plus FK-expanded
// join context — everything needed to answer the query.
// Text() prepends a notation legend explaining every symbol.
fmt.Println(result.Matched().Text())
}
Output:
Example (BuildAsync) ¶
This example demonstrates non-blocking startup with BuildAsync. The index builds in a background goroutine while the application continues setup. Queries block automatically until the index is ready.
package main
import (
"context"
"fmt"
"log"
"github.com/shrsv/dbctx"
)
func main() {
ctx := context.Background()
dsn := "postgres://localhost/mydb"
idx, ready, err := dbctx.BuildAsync(ctx, dsn, nil)
if err != nil {
log.Fatal(err)
}
defer idx.Close()
// Register idx with your application immediately.
go func() {
<-ready
log.Println("dbctx index is ready")
}()
// This call blocks automatically if the index isn't ready yet.
result, err := idx.Query("active users")
if err != nil {
log.Fatal(err)
}
fmt.Println(result.Matched().Text())
}
Output:
Example (Persist) ¶
This example demonstrates saving an index to a .dtx file and opening it later without a PostgreSQL connection.
package main
import (
"context"
"fmt"
"log"
"github.com/shrsv/dbctx"
)
func main() {
ctx := context.Background()
dsn := "postgres://localhost/mydb"
// Build and save to disk.
idx, err := dbctx.Build(ctx, dsn, &dbctx.Options{Path: "mydb.dtx"})
if err != nil {
log.Fatal(err)
}
idx.Close()
// Reopen without PostgreSQL.
idx, err = dbctx.Open("mydb.dtx")
if err != nil {
log.Fatal(err)
}
defer idx.Close()
tables, _ := idx.Tables()
fmt.Printf("Index has %d tables\n", len(tables))
}
Output:
Example (Selection) ¶
This example demonstrates the Selection API for filtering and rendering query results in different ways.
package main
import (
"context"
"fmt"
"log"
"github.com/shrsv/dbctx"
)
func main() {
ctx := context.Background()
idx, err := dbctx.Build(ctx, "postgres://localhost/mydb", nil)
if err != nil {
log.Fatal(err)
}
defer idx.Close()
result, err := idx.Query("failed reviews")
if err != nil {
log.Fatal(err)
}
// Matched + FK-expanded tables, with notation legend.
fmt.Println(result.Matched().Text())
// Without legend (tighter token budget).
fmt.Println(result.Matched().TextRaw())
// Only tables that scored directly, excluding FK-expanded join context.
fmt.Println(result.ScoredOnly().TextRaw())
// Specific tables by name.
fmt.Println(result.Include("reviews", "orgs").TextRaw())
// Matched minus a table.
fmt.Println(result.Matched().Exclude("migrations").TextRaw())
}
Output:
Index ¶
- func ComputeFingerprint(ext *schema.ExtractedSchema) string
- func LiveFingerprint(ctx context.Context, dsn string, opts *Options) (string, error)
- func StoreFingerprint(store *db.Store, fingerprint string) error
- type ColumnDetail
- type ColumnInfo
- type FKInfo
- type Index
- func (idx *Index) Close() error
- func (idx *Index) Err() error
- func (idx *Index) ImportTerminology(data []byte) (*TerminologyImportResult, error)
- func (idx *Index) ImportTerminologyFile(path string) (*TerminologyImportResult, error)
- func (idx *Index) ImportTerminologyGroups(groups []TerminologyGroup) (*TerminologyImportResult, error)
- func (idx *Index) Query(query string) (*ResultSet, error)
- func (idx *Index) Ready() <-chan struct{}
- func (idx *Index) Report(w io.Writer) error
- func (idx *Index) SchemaFingerprint() (string, error)
- func (idx *Index) Stats() (*Stats, error)
- func (idx *Index) TableDetail(name string) (*TableDetail, error)
- func (idx *Index) Tables() ([]TableSummary, error)
- func (idx *Index) Terminology() ([]TerminologyEntry, error)
- func (idx *Index) TerminologyPrompt() (string, error)
- type JSONBPathInfo
- type Options
- type QueryTiming
- type RejectedTerminology
- type ResultSet
- type ScoreBreakdown
- type Selection
- type SemanticContribution
- type SemanticHit
- type SignalContribution
- type Stats
- type TableContext
- type TableDetail
- type TableSummary
- type TerminologyEntry
- type TerminologyGroup
- type TerminologyImportResult
- type ValueInfo
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ComputeFingerprint ¶ added in v0.1.4
func ComputeFingerprint(ext *schema.ExtractedSchema) string
ComputeFingerprint hashes the table/column *shape* of an extracted schema - (schema, table, column, data type, nullable) tuples, sorted for determinism - into a short hex digest. Deliberately excludes constraints, indexes, and row estimates: those can churn (a new index, a changing row count) without making dbctx's own retrieval or schema text wrong, so counting them as drift would force unnecessary rebuilds. Only a change that would actually make an already-built .dtx describe the database incorrectly - an added/dropped/retyped column or table - changes this value.
func LiveFingerprint ¶ added in v0.1.4
LiveFingerprint computes a schema fingerprint fresh against a live PostgreSQL database, without opening or writing a .dtx file - the read-only counterpart to the fingerprint Build/BuildAsync store automatically. Compare its result against an already-built index's Index.SchemaFingerprint to detect schema drift cheaply: this only runs the same lightweight schema-extraction query Build's own first phase does, not a full rebuild (no field statistics, no JSONB sampling, no semantic embedding).
opts may be nil; only Schemas and MaxConns are consulted (Path, Logger, and NoSemantic don't apply here - nothing is written or embedded).
Types ¶
type ColumnDetail ¶
type ColumnDetail struct {
Name string `json:"name"`
Type string `json:"type"`
Nullable bool `json:"nullable"`
IsPK bool `json:"is_pk"`
FKTarget string `json:"fk_target,omitempty"`
Distinct int `json:"distinct"`
IsState bool `json:"is_state"`
IsCategoric bool `json:"is_categoric"`
Values []ValueInfo `json:"values,omitempty"`
JSONBPaths []JSONBPathInfo `json:"jsonb_paths,omitempty"`
}
ColumnDetail describes a column in a table detail response. It includes distinct count, state/categorical flags, representative values, and JSONB paths.
type ColumnInfo ¶
type ColumnInfo struct {
Name string `json:"name"`
Type string `json:"type"`
Nullable bool `json:"nullable"`
IsPK bool `json:"is_pk"`
FKTarget string `json:"fk_target,omitempty"`
IsState bool `json:"is_state"`
IsCategoric bool `json:"is_categoric"`
Values []ValueInfo `json:"values,omitempty"`
JSONBPaths []JSONBPathInfo `json:"jsonb_paths,omitempty"`
}
ColumnInfo describes a column in a query result, including its type, flags (PK, nullable, state, categorical), representative values, and JSONB paths if applicable.
type FKInfo ¶
type FKInfo struct {
SrcColumns string `json:"src_columns"`
RefTable string `json:"ref_table"`
DstColumns string `json:"dst_columns"`
}
FKInfo describes a foreign key relationship between tables.
type Index ¶
type Index struct {
// contains filtered or unexported fields
}
Index is a compiled database context index. It provides methods to query the database structure, relationships, field semantics, and representative values extracted from PostgreSQL.
An Index is safe for concurrent use by multiple goroutines. Create one with Build, BuildAsync, or Open.
func Build ¶
Build connects to PostgreSQL and builds a complete database context index.
It extracts schema, analyzes field statistics from pg_stats, discovers JSONB structure via sampling, and builds a full-text search index. The resulting index is ready for queries immediately upon return.
If opts is nil or opts.Path is empty, the index is stored in memory. Pass opts.Path to persist the index as a .dtx file on disk.
The caller must call Close on the returned Index when done.
func BuildAsync ¶
BuildAsync starts building the index in a background goroutine and returns immediately. The returned channel is closed when the build completes.
This is useful for non-blocking application startup. The returned Index can be registered with your application immediately. Any calls to Index.Query, Index.Tables, or other methods will block until the build completes.
Example:
idx, ready, err := dbctx.BuildAsync(ctx, dsn, nil)
if err != nil {
log.Fatal(err)
}
defer idx.Close()
// Register idx with your app immediately...
// Wait for readiness:
<-ready
If the build fails, Index.Err returns the error and Query/Tables/etc will return that error.
func Open ¶
Open opens an existing .dtx file for querying. The file must exist and contain a valid dbctx index created by Build or the `dbctx build` CLI.
The caller must call Close on the returned Index when done.
Example ¶
This example demonstrates opening a persisted .dtx file and listing all tables with summary information.
package main
import (
"fmt"
"log"
"github.com/shrsv/dbctx"
)
func main() {
idx, err := dbctx.Open("mydb.dtx")
if err != nil {
log.Fatal(err)
}
defer idx.Close()
tables, err := idx.Tables()
if err != nil {
log.Fatal(err)
}
for _, t := range tables {
fmt.Printf("%-30s %6.0f rows %d cols %d FKs\n",
t.Name, t.RowEstimate, t.ColCount, t.FKCount)
}
}
Output:
func (*Index) Close ¶
Close releases all resources held by the index, including the underlying SQLite database connection. After Close, no other methods may be called.
func (*Index) Err ¶
Err returns the build error if an async build failed. Returns nil if the build succeeded or is still in progress. Check Index.Ready first to know when the build is done.
func (*Index) ImportTerminology ¶ added in v0.1.1
func (idx *Index) ImportTerminology(data []byte) (*TerminologyImportResult, error)
ImportTerminology validates and persists a terminology dictionary — typically produced by working through the prompt from Index.TerminologyPrompt with an external LLM — into this index.
data must be a JSON array of term groups:
[{"term": "loc", "aliases": ["lines of code"], "targets": ["metrics.loc"]}]
data is just bytes: a JSON string literal works fine as []byte(jsonString), a value read from a file, an HTTP request body, or anything else that ends up as a []byte — there's no separate string-typed variant of this method because none is needed. If you already have a file on disk, Index.ImportTerminologyFile saves the os.ReadFile boilerplate; if you already have Go values instead of JSON text (e.g. built programmatically), use Index.ImportTerminologyGroups to skip the JSON round-trip entirely.
Every alias/target pair is validated against the actual schema before being persisted; entries that don't resolve to a real table, column, or JSONB path are rejected individually (reported in the result) rather than failing the whole import. Terminology is purely additive to retrieval — see the package documentation — and is never required.
func (*Index) ImportTerminologyFile ¶ added in v0.1.1
func (idx *Index) ImportTerminologyFile(path string) (*TerminologyImportResult, error)
ImportTerminologyFile reads path and passes its contents to Index.ImportTerminology — the same JSON format, just read from disk for convenience instead of requiring the caller to os.ReadFile it first. This is what `dbctx terminology import` uses internally.
func (*Index) ImportTerminologyGroups ¶ added in v0.1.1
func (idx *Index) ImportTerminologyGroups(groups []TerminologyGroup) (*TerminologyImportResult, error)
ImportTerminologyGroups validates and persists terminology supplied as Go values rather than JSON text — for callers building a dictionary programmatically (from their own data source, a different format, code generation, ...) who would otherwise have to marshal it to JSON just to call Index.ImportTerminology. Validation and persistence behavior are identical either way; this only changes how the input arrives.
func (*Index) Query ¶
Query searches the index for tables matching the given natural language query. It combines full-text search, fuzzy table name matching, value matching, and foreign-key expansion to find relevant tables and their context.
If the index was created with BuildAsync and the build is still in progress, Query blocks until the build completes.
Returns a ResultSet that can be filtered and converted to compact text:
result, _ := idx.Query("failed reviews last month")
text := result.Matched().Text() // matched + FK-expanded tables
text := result.ScoredOnly().Text() // only tables that scored directly
text := result.Include("reviews").Text() // specific tables
func (*Index) Ready ¶
func (idx *Index) Ready() <-chan struct{}
Ready returns a channel that is closed when the index is ready for queries. For synchronous builds created with Build, the channel is already closed. For async builds created with BuildAsync, the channel closes when the background build completes.
func (*Index) Report ¶
Report writes a human-readable report of the entire index to w. The report includes schema, state fields, categorical fields, JSONB structure, relationships, and summary statistics.
Blocks until the index is ready if an async build is in progress.
func (*Index) SchemaFingerprint ¶ added in v0.1.4
SchemaFingerprint returns the schema fingerprint stored in this index at build time - see LiveFingerprint for the counterpart that recomputes one fresh against a live database, and compare the two to detect whether a .dtx has gone stale relative to the schema it describes. Returns "" (no error) if the index predates this feature and was never given a fingerprint.
Blocks until the index is ready if an async build is in progress.
Example ¶
ExampleIndex_SchemaFingerprint detects whether a .dtx file has gone stale relative to the live database it describes, by comparing the fingerprint stored at build time against one computed fresh right now.
package main
import (
"context"
"fmt"
"log"
"github.com/shrsv/dbctx"
)
func main() {
ctx := context.Background()
dsn := "postgres://localhost/mydb"
idx, err := dbctx.Open("mydb.dtx")
if err != nil {
log.Fatal(err)
}
defer idx.Close()
stored, err := idx.SchemaFingerprint()
if err != nil {
log.Fatal(err)
}
live, err := dbctx.LiveFingerprint(ctx, dsn, nil)
if err != nil {
log.Fatal(err)
}
if stored != live {
fmt.Println("schema has drifted since this .dtx was built - rebuild before trusting it")
} else {
fmt.Println("schema fingerprint matches - .dtx is up to date")
}
}
Output:
func (*Index) Stats ¶
Stats returns summary statistics about the index, including counts of tables, columns, foreign keys, state fields, categorical fields, JSONB paths, and field values.
Blocks until the index is ready if an async build is in progress.
Example ¶
This example demonstrates getting summary statistics about the index.
package main
import (
"context"
"fmt"
"log"
"github.com/shrsv/dbctx"
)
func main() {
ctx := context.Background()
idx, err := dbctx.Build(ctx, "postgres://localhost/mydb", nil)
if err != nil {
log.Fatal(err)
}
defer idx.Close()
stats, err := idx.Stats()
if err != nil {
log.Fatal(err)
}
fmt.Printf("Tables: %d\n", stats.Tables)
fmt.Printf("Columns: %d\n", stats.Columns)
fmt.Printf("Foreign keys: %d\n", stats.ForeignKeys)
fmt.Printf("State fields: %d\n", stats.StateFields)
fmt.Printf("Categorical fields: %d\n", stats.CategoricalFields)
fmt.Printf("JSONB paths: %d\n", stats.JSONBPaths)
}
Output:
func (*Index) TableDetail ¶
func (idx *Index) TableDetail(name string) (*TableDetail, error)
TableDetail returns detailed information about a specific table, including columns with types, PK/FK tags, value distributions, JSONB paths, and foreign key relationships.
Returns nil and no error if the table is not found. Blocks until the index is ready if an async build is in progress.
Example ¶
This example demonstrates getting detailed information about a single table including columns, types, primary keys, foreign keys, and representative values for state-like fields.
package main
import (
"context"
"fmt"
"log"
"strings"
"github.com/shrsv/dbctx"
)
func main() {
ctx := context.Background()
idx, err := dbctx.Build(ctx, "postgres://localhost/mydb", nil)
if err != nil {
log.Fatal(err)
}
defer idx.Close()
detail, err := idx.TableDetail("reviews")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Table: %s\n", detail.Name)
fmt.Printf("Primary key: %s\n", strings.Join(detail.PrimaryKey, ", "))
fmt.Printf("Columns: %d\n", len(detail.Columns))
for _, col := range detail.Columns {
flags := ""
if col.IsPK {
flags += " PK"
}
if col.IsState {
flags += " [state]"
}
if col.FKTarget != "" {
flags += " -> " + col.FKTarget
}
fmt.Printf(" %-20s %-20s%s\n", col.Name, col.Type, flags)
}
}
Output:
func (*Index) Tables ¶
func (idx *Index) Tables() ([]TableSummary, error)
Tables returns a summary of all tables in the index. Each entry includes the table name, schema, row estimate, column count, and FK count.
Blocks until the index is ready if an async build is in progress.
func (*Index) Terminology ¶ added in v0.1.1
func (idx *Index) Terminology() ([]TerminologyEntry, error)
Terminology returns every currently-imported terminology entry, for inspection — so the user-supplied mappings influencing retrieval are never a black box. Returns an empty slice if none have been imported.
func (*Index) TerminologyPrompt ¶ added in v0.1.1
TerminologyPrompt generates a self-contained prompt that can be pasted into a large external LLM (Claude, GPT, Gemini, or similar) to interactively derive a terminology dictionary for this database — a mapping from domain vocabulary (abbreviations, acronyms, business jargon) to the exact schema objects it refers to. dbctx never calls an LLM itself; this only produces text for the caller to use however they like (print it, copy it, pipe it into their own LLM integration).
The prompt embeds the complete schema this Index already knows — tables, columns, relationships, state/categorical values, and JSONB structure — so it is usable on its own without additional context.
See Index.ImportTerminology to load the LLM's resulting output back into the index.
type JSONBPathInfo ¶
type JSONBPathInfo struct {
Path string `json:"path"`
InferredType string `json:"inferred_type"`
SampleValues string `json:"sample_values,omitempty"`
}
JSONBPathInfo describes a path within a JSONB column, including its inferred type and sample values.
type Options ¶
type Options struct {
// Path is the file path for the .dtx file. If empty, an in-memory
// SQLite database is used (no file created). In-memory indexes are
// faster but must be rebuilt on each process start.
Path string
// Schemas is a comma-separated list of PostgreSQL schemas to extract.
// Defaults to "public" if empty.
Schemas string
// MaxConns is the maximum number of concurrent PostgreSQL connections
// in the connection pool. Higher values allow more parallel JSONB
// analysis. Defaults to 4 if zero.
MaxConns int
// Logger receives progress messages during build. If nil, os.Stderr is used.
Logger io.Writer
// NoSemantic disables building the optional local embedding-based
// semantic index. By default (false), Build downloads (if not already
// cached — see internal/embed) and runs a small local embedding model
// to add a semantic retrieval signal alongside dbctx's existing
// lexical/fuzzy matching, improving recall for paraphrased queries
// (e.g. "buyers" finding a "customers" table). This never replaces
// lexical matching, only augments it — see [Index.Query].
//
// If the model or its inference runtime can't be obtained or loaded
// (offline, unsupported platform, etc.), Build logs a warning and
// continues with a lexical-only index rather than failing — semantic
// indexing is always best-effort.
NoSemantic bool
}
Options configures how a database context index is built.
type QueryTiming ¶ added in v0.1.1
type QueryTiming struct {
LexicalMs float64 `json:"lexical_ms"`
SemanticMs float64 `json:"semantic_ms"`
SemanticRan bool `json:"semantic_ran"`
ExpandMs float64 `json:"expand_ms"`
TotalMs float64 `json:"total_ms"`
}
QueryTiming records how long each phase of a query took, in milliseconds. SemanticRan distinguishes "semantic search ran and took SemanticMs" from "semantic search did not run at all" (SemanticMs left at zero either way).
type RejectedTerminology ¶ added in v0.1.1
type RejectedTerminology struct {
Term string `json:"term"`
Alias string `json:"alias"`
Target string `json:"target"`
Reason string `json:"reason"`
}
RejectedTerminology records one terminology mapping ImportTerminology refused to persist, and why (e.g. the target doesn't resolve to a real schema object) — so a partially-rejected import is inspectable rather than silently dropping entries.
type ResultSet ¶
type ResultSet struct {
// Query is the original query string.
Query string `json:"query"`
// Tables contains all tables in the result, including both directly
// matched tables (score > 0) and FK-expanded tables (score = 0).
Tables []TableContext `json:"tables"`
// SemanticHits lists the evidence the optional semantic retrieval
// signal contributed to this result, if semantic search was available
// and ran. Empty when semantic search is disabled/unavailable, or when
// it ran but found nothing above the other tables already found
// lexically. This exists so a result's ranking is inspectable — why a
// table appeared even without a lexical match — not just a black-box
// score. See the design principle: "Engineers should be able to
// understand why a particular table or field appeared."
SemanticHits []SemanticHit `json:"semantic_hits,omitempty"`
// Timing breaks down how long each phase of the query took, so
// latency is inspectable the same way scoring is.
Timing QueryTiming `json:"timing"`
}
ResultSet holds the results of a query and provides methods to select subsets of matched tables and render them as compact text.
The typical flow is:
result, _ := idx.Query("failed reviews")
text := result.Matched().Text() // compact schema: matched + FK-expanded
func (*ResultSet) Include ¶
Include returns a Selection containing only the named tables. Tables not found in the result set are silently ignored.
func (*ResultSet) Matched ¶
Matched returns a Selection containing every table relevant to competently answering the query: tables that scored a direct hit from the retrieval signals (fuzzy, FTS, value, terminology, semantic), plus tables pulled in via foreign-key expansion so join context isn't silently dropped. This is the recommended default for feeding an LLM or text-to-SQL system — see ResultSet.ScoredOnly for just the directly-scored subset, with FK context excluded.
func (*ResultSet) ScoredOnly ¶ added in v0.1.2
ScoredOnly returns a Selection containing only tables that scored a direct match (MatchScore > 0) from the retrieval signals, excluding tables pulled in purely via foreign-key expansion. Use this for the narrow "what literally matched" view — e.g. inspecting retrieval quality — rather than ResultSet.Matched's full join-ready context.
func (*ResultSet) TableMap ¶
func (rs *ResultSet) TableMap() map[string]TableContext
TableMap returns a map of table name to TableContext for quick lookup.
type ScoreBreakdown ¶ added in v0.1.1
type ScoreBreakdown struct {
FTS SignalContribution `json:"fts"`
Fuzzy SignalContribution `json:"fuzzy"`
Value SignalContribution `json:"value"`
Terminology SignalContribution `json:"terminology"`
LexicalTotal float64 `json:"lexical_total"`
Semantic *SemanticContribution `json:"semantic,omitempty"`
FinalScore float64 `json:"final_score"`
}
ScoreBreakdown is the "show your work" behind TableContext.MatchScore: every lexical signal's raw score, its fixed weight, and the resulting weighted contribution, plus the semantic signal's contribution if one ran. See search.ScoreBreakdown (internal/search) for the full formula documentation this mirrors.
type Selection ¶
type Selection struct {
// contains filtered or unexported fields
}
Selection represents a subset of tables from a ResultSet. It provides methods to refine the selection and render it as compact text suitable for passing to an LLM or text-to-SQL system.
func (*Selection) Include ¶
Include adds the named tables to the selection. Tables not in the result set are silently ignored.
func (*Selection) Tables ¶
func (s *Selection) Tables() []TableContext
Tables returns the TableContext objects in this selection, in the same order they appear in the original result set.
func (*Selection) Text ¶
Text renders the selected tables as compact, human-readable text with a notation legend at the top. The legend explains every symbol and annotation used in the output so that an LLM (or human) can interpret the schema without external documentation.
Use Selection.TextRaw to omit the legend.
func (*Selection) TextRaw ¶
TextRaw renders the selected tables as compact, human-readable text without the notation legend. Use this when the caller already knows the notation, or when token budget is tight and the legend would be wasted context.
The output includes table names, scores, primary keys, foreign keys, columns with type/flags, state/categorical values, and JSONB paths.
type SemanticContribution ¶ added in v0.1.1
type SemanticContribution struct {
Cosine float64 `json:"cosine"`
Normalized float64 `json:"normalized"`
Weight float64 `json:"weight"`
Scale float64 `json:"scale"`
Contribution float64 `json:"contribution"`
EvidenceKind string `json:"evidence_kind"`
EvidenceText string `json:"evidence_text"`
}
SemanticContribution documents how the optional semantic signal contributed to a table's final score: contribution = Weight * Normalized * Scale. Cosine is the best-matching embedded object's raw similarity to the query; Normalized is that score after query-relative min-max normalization; Scale is the strongest lexical score found anywhere in the query (or 1.0 if lexical found nothing at all).
type SemanticHit ¶ added in v0.1.1
type SemanticHit struct {
TableName string `json:"table_name"`
Kind string `json:"kind"`
Text string `json:"text"`
Score float64 `json:"score"`
}
SemanticHit is one piece of evidence the semantic retrieval signal contributed: the best-matching embedded schema object for a table and its similarity to the query.
type SignalContribution ¶ added in v0.1.1
type SignalContribution struct {
Raw float64 `json:"raw"`
Weight float64 `json:"weight"`
Contribution float64 `json:"contribution"`
}
SignalContribution is one lexical signal's raw score, its fixed weight, and the resulting weighted contribution (Raw * Weight).
type Stats ¶
type Stats struct {
Tables int `json:"tables"`
Columns int `json:"columns"`
ForeignKeys int `json:"foreign_keys"`
StateFields int `json:"state_fields"`
CategoricalFields int `json:"categorical_fields"`
JSONBPaths int `json:"jsonb_paths"`
FieldValues int `json:"field_values"`
}
Stats contains summary statistics about a database context index.
type TableContext ¶
type TableContext struct {
TableName string `json:"table_name"`
Schema string `json:"schema"`
Columns []ColumnInfo `json:"columns"`
PrimaryKey []string `json:"primary_key"`
ForeignKeys []FKInfo `json:"foreign_keys"`
IsMatch bool `json:"is_match"`
MatchScore float64 `json:"match_score"`
// Score documents exactly how MatchScore was computed, signal by
// signal (FTS, fuzzy, value, terminology, and — if it ran — semantic).
// Nil for tables that were only pulled in via foreign-key expansion.
Score *ScoreBreakdown `json:"score,omitempty"`
}
TableContext represents a table in a query result with its relevance score and full context (columns, values, relationships, JSONB paths).
type TableDetail ¶
type TableDetail struct {
TableSummary
PrimaryKey []string `json:"primary_key"`
ForeignKeys []FKInfo `json:"foreign_keys"`
Columns []ColumnDetail `json:"columns"`
}
TableDetail contains complete information about a table, including columns with types, flags, values, JSONB paths, and all relationships.
type TableSummary ¶
type TableSummary struct {
ID int `json:"id"`
Schema string `json:"schema"`
Name string `json:"name"`
RowEstimate float64 `json:"row_estimate"`
ColCount int `json:"columns"`
FKCount int `json:"fk_count"`
}
TableSummary is a lightweight table descriptor returned by Index.Tables.
type TerminologyEntry ¶ added in v0.1.1
type TerminologyEntry struct {
Term string `json:"term"`
Alias string `json:"alias"`
TargetTable string `json:"target_table"`
TargetColumn string `json:"target_column,omitempty"`
TargetPath string `json:"target_path,omitempty"`
Source string `json:"source"`
ImportedAt string `json:"imported_at,omitempty"`
}
TerminologyEntry is one user-approved (alias -> schema object) mapping, as returned by Index.Terminology.
type TerminologyGroup ¶ added in v0.1.1
type TerminologyGroup struct {
Term string `json:"term"`
Aliases []string `json:"aliases"`
Targets []string `json:"targets"`
}
TerminologyGroup is one term with all of its human-language aliases and the exact schema objects it refers to — the unit of input Index.ImportTerminologyGroups accepts, and the Go-value equivalent of one entry in the JSON array Index.ImportTerminology parses:
dbctx.TerminologyGroup{
Term: "loc",
Aliases: []string{"lines of code", "source lines of code"},
Targets: []string{"metrics.loc"},
}
Targets use dbctx's "table" / "table.column" / "table.column:$.json.path" notation (see Index.TerminologyPrompt's generated instructions) and are validated against the actual schema on import — see Index.ImportTerminologyGroups.
type TerminologyImportResult ¶ added in v0.1.1
type TerminologyImportResult struct {
Accepted int `json:"accepted"`
Rejected []RejectedTerminology `json:"rejected,omitempty"`
}
TerminologyImportResult summarizes an Index.ImportTerminology call.
Directories
¶
| Path | Synopsis |
|---|---|
|
dbctx
command
|
|
|
internal
|
|
|
embed
Package embed provides local, CGO-based inference for the BGE-small-en-v1.5 sentence embedding model via the ONNX Runtime, plus the machinery to download and cache the model weights and the onnxruntime shared library on first use.
|
Package embed provides local, CGO-based inference for the BGE-small-en-v1.5 sentence embedding model via the ONNX Runtime, plus the machinery to download and cache the model weights and the onnxruntime shared library on first use. |
|
semantic
Package semantic adds an optional embedding-based retrieval signal on top of dbctx's existing deterministic/lexical index.
|
Package semantic adds an optional embedding-based retrieval signal on top of dbctx's existing deterministic/lexical index. |
|
terminology
Package terminology implements dbctx's optional, user-controlled terminology layer: a mapping from domain vocabulary (abbreviations, acronyms, business jargon, natural-language descriptions) that a human might type into a query, to the exact dbctx schema object it refers to.
|
Package terminology implements dbctx's optional, user-controlled terminology layer: a mapping from domain vocabulary (abbreviations, acronyms, business jargon, natural-language descriptions) that a human might type into a query, to the exact dbctx schema object it refers to. |
|
testutil
Package testutil provides shared test helpers for dbctx tests.
|
Package testutil provides shared test helpers for dbctx tests. |


Table details
JSONB expansion
State & categorical values
Query interface




