Skip to main content
The core package ships two complementary search engines:
  • SearchEngine — fuzzy text search via Fuse.js. No server required, runs entirely in-memory.
  • SemanticSearchEngine — vector similarity search using pre-computed embeddings.
Both are exported from the @understand-anything/core/search subpath (browser-safe) and from the main @understand-anything/core entry.
import { SearchEngine, SemanticSearchEngine } from '@understand-anything/core/search';

SearchEngine

Fuzzy full-text search over a collection of GraphNode objects.

Constructor

constructor(nodes: GraphNode[])
nodes
GraphNode[]
required
Initial set of graph nodes to index. The Fuse.js index is built immediately on construction.
search(query: string, options?: SearchOptions): SearchResult[]
Searches the indexed nodes and returns ranked results. Returns an empty array when query is blank or whitespace-only. How it works:
  1. The query is trimmed.
  2. Space-separated tokens are joined with | (logical OR) to produce a Fuse.js extended search expression — e.g. "auth contrl" becomes "auth | contrl". This means a node matches if it contains any of the tokens, making the search tolerant of typos across separate words.
  3. Results are filtered by options.types if provided, then sliced to options.limit.
query
string
required
Search query string. Multi-word queries match nodes that contain any of the words (OR semantics).
options
SearchOptions
Optional search configuration.
Returns SearchResult[]
nodeId
string
required
The id of the matching GraphNode.
score
number
required
Fuse.js match score. 0 is a perfect match, 1 is the worst possible match. Results are returned in ascending score order (best first).

updateNodes()

updateNodes(nodes: GraphNode[]): void
Replaces the indexed node collection and rebuilds the Fuse.js index. Use this after the knowledge graph is reloaded or updated.
nodes
GraphNode[]
required
New complete set of nodes to index.

Fuse.js Configuration

The Fuse.js instance is configured with the following options, applied consistently across all SearchEngine instances:
OptionValueEffect
threshold0.4Controls fuzziness. Lower = stricter. 0.4 allows moderate typos.
ignoreLocationtrueMatches anywhere in the string, not just near the start.
useExtendedSearchtrueEnables the `` OR operator in query strings.
includeScoretruePopulates score on each result.
Field weights:
FieldWeightRationale
name0.4Symbol names are the most direct match signal
tags0.3Keyword tags boost topical relevance
summary0.2Prose description provides broader coverage
languageNotes0.1Language idiom notes contribute marginally

SearchResult

export interface SearchResult {
  nodeId: string;
  score: number; // 0 = perfect match, 1 = worst match
}
Returned by both SearchEngine.search() and SemanticSearchEngine.search().

SearchOptions

export interface SearchOptions {
  types?: GraphNode["type"][];
  limit?: number;
}
Used exclusively by SearchEngine.search().

Usage example

import { SearchEngine } from '@understand-anything/core/search';
import type { GraphNode } from '@understand-anything/core/types';

const nodes: GraphNode[] = graph.nodes;
const engine = new SearchEngine(nodes);

// Search all node types
const results = engine.search('auth middleware');
// results[0].score === 0   → perfect match
// results[0].nodeId        → e.g. 'func:src/middleware/auth.ts:verifyJwt'

// Search only function nodes, limit to 10
const fnResults = engine.search('validate', { types: ['function'], limit: 10 });

// Rebuild index after graph reload
engine.updateNodes(updatedGraph.nodes);

SemanticSearchEngine

Vector similarity search using pre-computed embeddings. Complements SearchEngine when embedding data is available.
import { SemanticSearchEngine } from '@understand-anything/core/search';

Constructor

constructor(nodes: GraphNode[], embeddings: Record<string, number[]>)
nodes
GraphNode[]
required
The graph nodes to search over.
embeddings
Record<string, number[]>
required
Pre-computed embedding vectors keyed by GraphNode.id. Nodes without a corresponding embedding are skipped during search.

search()

search(queryEmbedding: number[], options?: SemanticSearchOptions): SearchResult[]
Ranks nodes by cosine similarity to the query embedding. Results are returned in ascending score order (best first), where score = 1 - cosineSimilarity so that 0 still means a perfect match, consistent with SearchResult.
queryEmbedding
number[]
required
Embedding vector for the query, produced by the same model used to embed the nodes.
options
SemanticSearchOptions
Optional search configuration.
Returns SearchResult[] — same shape as SearchEngine.search().

hasEmbeddings()

hasEmbeddings(): boolean
Returns true if any embeddings have been stored. Use this to decide whether to fall back to fuzzy search.

addEmbedding()

addEmbedding(nodeId: string, embedding: number[]): void
Adds or replaces the embedding for a single node. Useful for incrementally updating embeddings without rebuilding the entire engine.
nodeId
string
required
The GraphNode.id to associate the embedding with.
embedding
number[]
required
The embedding vector.

updateNodes()

updateNodes(nodes: GraphNode[]): void
Replaces the node list. Existing embeddings are preserved.
nodes
GraphNode[]
required
New complete set of nodes.

cosineSimilarity()

Utility function exported alongside the search engines.
export function cosineSimilarity(a: number[], b: number[]): number
Computes the cosine similarity between two equal-length vectors. Returns 0 if either vector has zero magnitude.
import { cosineSimilarity } from '@understand-anything/core/search';

const sim = cosineSimilarity([1, 0, 0], [1, 0, 0]); // 1.0
const sim2 = cosineSimilarity([1, 0, 0], [0, 1, 0]); // 0.0

SemanticSearchEngine usage example

import { SemanticSearchEngine } from '@understand-anything/core/search';

// embeddings produced externally (e.g. OpenAI text-embedding-3-small)
const embeddings: Record<string, number[]> = {
  'file:src/auth.ts': [0.12, 0.98, ...],
  'func:src/auth.ts:validateToken': [0.11, 0.97, ...],
};

const engine = new SemanticSearchEngine(graph.nodes, embeddings);

if (engine.hasEmbeddings()) {
  const queryVec = await embed('authentication middleware');
  const results = engine.search(queryVec, { limit: 5, threshold: 0.7 });
}

Build docs developers (and LLMs) love