SearchResult
SearchResult
Data transfer objects and types for full-text search requests and responses flowing between the backend search engine, HTTP handler, and frontend client.
- Go source:
backend/internal/search/types.go - TypeScript source:
src/lib/types/search.ts
Name Mapping (Go / TypeScript)
The Go backend and TypeScript frontend define mirrored types with slightly different naming conventions. The JSON wire format is identical.
| Go Struct | TypeScript Interface | Purpose |
|---|---|---|
SearchResult |
SearchResponse |
Full search response envelope |
SearchResultItem |
SearchResultItem |
Single search hit |
SearchOptions |
SearchParams |
Request parameters / filters |
| — | SearchResultType |
Union type 'content' | 'glossary' (Go uses plain string) |
SearchResult / SearchResponse
Holds the complete response for a search query, including paginated results, total count, and per-type facet counts.
Go Struct
type SearchResult struct {
Results []SearchResultItem `json:"results"` // Ordered by relevance score.
Total int `json:"total"` // Total match count (for pagination math).
Facets map[string]int `json:"facets"` // Per-type counts: {"content": 30, "glossary": 12}.
Message string `json:"message,omitempty"` // Optional info message (e.g. "Scope Not Found").
}
TypeScript Interface
export interface SearchResponse {
results: SearchResultItem[];
total: number;
facets: Record<string, number>;
message?: string;
}
Fields
| Field | Go Type | TS Type | Required | Default | JSON Key | Description |
|---|---|---|---|---|---|---|
Results / results |
[]SearchResultItem |
SearchResultItem[] |
yes | [] |
results |
Ordered list of search hits, ranked by BM25 relevance score |
Total / total |
int |
number |
yes | 0 |
total |
Total number of matches across all pages (used for pagination math) |
Facets / facets |
map[string]int |
Record<string, number> |
yes | {} |
facets |
Per-type hit counts, e.g. {"content": 30, "glossary": 12} |
Message / message |
string |
string? |
no | "" |
message |
Optional informational message (e.g. "Scope Not Found"). Omitted from JSON when empty. |
SearchResultItem
A single search hit returned by the FTS5 search engine.
Go Struct
type SearchResultItem struct {
Type string `json:"type"` // "content" or "glossary"
ID string `json:"id"` // Entity UUID
Title string `json:"title"` // Content title or glossary term
Snippet string `json:"snippet"` // Highlighted excerpt with <mark> tags
Score float64 `json:"score"` // FTS5 BM25 rank score (lower = more relevant)
SlugPath string `json:"slugPath,omitempty"` // Content slug path (empty for glossary)
}
TypeScript Interface
export type SearchResultType = 'content' | 'glossary';
export interface SearchResultItem {
type: SearchResultType;
id: string;
title: string;
snippet: string;
score: number;
slugPath?: string;
}
Fields
| Field | Go Type | TS Type | Required | JSON Key | Description |
|---|---|---|---|---|---|
Type / type |
string |
SearchResultType |
yes | type |
Discriminator: "content" or "glossary" |
ID / id |
string |
string |
yes | id |
Entity UUID of the matched content or glossary entry |
Title / title |
string |
string |
yes | title |
Content document title or glossary term |
Snippet / snippet |
string |
string |
yes | snippet |
Highlighted excerpt with <mark> tags generated by FTS5 snippet() |
Score / score |
float64 |
number |
yes | score |
FTS5 BM25 relevance rank (lower value = more relevant) |
SlugPath / slugPath |
string |
string? |
no | slugPath |
Content slug path (e.g. "series-1/book-1/chapter-3"). Omitted from JSON for glossary results. |
SearchOptions / SearchParams
Controls filtering and pagination for a search request.
Go Struct
type SearchOptions struct {
Types []string // Filter by type: "content", "glossary". Empty = all types.
Limit int // Maximum results to return. Default 20.
Offset int // Pagination offset.
}
TypeScript Interface
export interface SearchParams {
query: string;
types?: SearchResultType[];
limit?: number;
offset?: number;
}
Fields
| Field | Go Type | TS Type | Required | Default | Description |
|---|---|---|---|---|---|
— / query |
— | string |
yes (TS only) | — | Search query string. May include scope prefix (e.g. "book-1:dragon"). In Go, query is a separate method parameter, not part of the options struct. |
Types / types |
[]string |
SearchResultType[]? |
no | all types | Filter results to specific types: ["content"], ["glossary"], or both |
Limit / limit |
int |
number? |
no | 20 |
Maximum results per page (max 100) |
Offset / offset |
int |
number? |
no | 0 |
Pagination offset |
In Go, the search query string is passed as a separate query parameter to SearchProvider.Search(). In TypeScript, query is included directly in the SearchParams interface for convenience when calling the API client.
Loader Callback Types
These function types are used during full reindexing (ReindexAll) to load all indexable data from the repository layer without creating a circular import between the search and repository packages.
ContentLoader
type ContentLoader func(ctx context.Context, projectSlug string) ([]ContentIndexData, error)
Loads all indexable content documents for a project. Called during ReindexAll to enumerate every document that should appear in the content_fts index.
Parameters:
ctx(context.Context) -- request contextprojectSlug(string) -- project identifier used to resolve the database
Returns: []ContentIndexData -- one entry per content document, or an error.
GlossaryLoader
type GlossaryLoader func(ctx context.Context, projectSlug string) ([]GlossaryIndexData, error)
Loads all indexable glossary entries for a project. Called during ReindexAll to enumerate every entry that should appear in the glossary_fts index.
Parameters:
ctx(context.Context) -- request contextprojectSlug(string) -- project identifier used to resolve the database
Returns: []GlossaryIndexData -- one entry per glossary entry, or an error.
Index Data Types
These structs carry the field data that gets written into the FTS5 virtual tables. They are produced by the loader callbacks and consumed by the search provider during indexing.
ContentIndexData
type ContentIndexData struct {
ID string // Content entity UUID
Title string // Document title
PlainText string // Plain text extracted from ProseMirror JSON
Tags string // Comma-separated tag list
SlugPath string // Full slug path for display (e.g. "series-1/book-1/chapter-3")
}
| Field | Type | Description |
|---|---|---|
ID |
string |
Content entity UUID |
Title |
string |
Document title |
PlainText |
string |
Plain text extracted from ProseMirror JSON (used for full-text indexing) |
Tags |
string |
Comma-separated tag list |
SlugPath |
string |
Full slug path for display (e.g. "series-1/book-1/chapter-3") |
GlossaryIndexData
type GlossaryIndexData struct {
ID string // Glossary entry UUID
Term string // Primary term
Definition string // Entry definition
Aliases string // Comma-separated alias list
Tags string // Comma-separated tag list
ScopeTargets string // Comma-separated scope target paths (empty until glossary-v2)
}
| Field | Type | Description |
|---|---|---|
ID |
string |
Glossary entry UUID |
Term |
string |
Primary glossary term |
Definition |
string |
Entry definition text |
Aliases |
string |
Comma-separated alias list |
Tags |
string |
Comma-separated tag list |
ScopeTargets |
string |
Comma-separated scope target paths (empty until glossary-v2) |
DBOpener
type DBOpener func(ctx context.Context, projectSlug string) (*sql.DB, error)
Function type that opens a raw *sql.DB connection for a given project. Injected into the FTS5 provider at initialization to decouple the search package from repository/sqlite.
Parameters:
ctx(context.Context) -- request contextprojectSlug(string) -- project slug used to resolve the database file path
Returns: an open *sql.DB connection (caller must close), or an error.
Constraints
SearchResultItem.Typemust be"content"or"glossary"SearchOptions.Limitdefaults to 20; API enforces a maximum of 100SearchResult.Facetskeys are always the set of searched typesScoreuses FTS5 BM25 ranking where lower values indicate higher relevance
Read By
- Search HTTP handler (builds
SearchResultfrom provider) - Search API client (deserializes
SearchResponse) - Search store (manages
SearchResponsestate)
Written By
- FTS5 provider (constructs
SearchResultandSearchResultItemfrom FTS5 query output) - Content repository (produces
ContentIndexDataviaContentLoader) - Glossary repository (produces
GlossaryIndexDataviaGlossaryLoader)
Transformations
- BM25 score inversion: FTS5 returns negative BM25 scores; the provider negates them so that lower positive values indicate higher relevance in the API response.
- Snippet highlighting: The FTS5
snippet()function wraps matched terms in<mark>tags. These HTML tags are passed through to the frontend for rendering. - Scope prefix parsing: When
SearchParams.querycontains a colon (e.g."book-1:dragon"), the backend splits it into a scope path and search terms before executing the FTS5 query.