How Semantic and Fuzzy Search Work in the Understand-Anything Dashboard
The Understand-Anything dashboard implements dual search strategies—fuzzy text matching via Fuse.js and semantic vector similarity via cosine comparison—that users toggle between using setSearchMode() in the SearchBar component.
The Understand-Anything repository provides a knowledge graph visualization tool where finding nodes efficiently is critical. The dashboard supports both traditional keyword search and AI-powered semantic retrieval, orchestrated through a Zustand store that delegates queries to either SearchEngine or SemanticSearchEngine depending on the active mode.
Understanding the Two Search Modes
The dashboard stores the current strategy in searchMode state and selects the appropriate engine at runtime.
Fuzzy Search with Fuse.js
The fuzzy search implementation lives in [packages/core/src/search.ts](https://github.com/Lum1104/Understand-Anything/blob/main/understand-anything-plugin/packages/core/src/search.ts) and leverages the Fuse.js library for approximate string matching.
- Indexed fields: The engine indexes
name,tags,summary, andlanguageNoteswith weights ranging from 0.4 to 0.1. - Query preprocessing: User input is trimmed, split on whitespace, and re-joined with
" | "to create an extended OR query (e.g.,auth | control). - Threshold: Fuse operates with a threshold of 0.4, where 0 represents a perfect match and 1 represents the worst possible match.
- Filtering: Results are optionally filtered by node type and limited to the requested count before returning matching node IDs.
Semantic Search with Vector Embeddings
The semantic search implementation is located in [packages/core/src/embedding-search.ts](https://github.com/Lum1104/Understand-Anything/blob/main/understand-anything-plugin/packages/core/src/embedding-search.ts).
- Embedding storage: Each graph node may have a pre-computed embedding stored as a
Map<string, number[]>. - Similarity calculation: The engine computes cosine similarity between the query vector and each node's vector using the
cosineSimilarityhelper function. - Score conversion: Raw similarity scores (0 to 1) are converted to Fuse-compatible scores via
1 - similarityto maintain consistent ranking semantics. - Threshold filtering: Only results meeting the configured similarity threshold (e.g., ≥ 0.4) are returned, sorted by relevance and sliced to the requested limit.
How the Search Flow Works in the Dashboard
The search orchestration happens in [packages/dashboard/src/store.ts](https://github.com/Lum1104/Understand-Anything/blob/main/understand-anything-plugin/packages/dashboard/src/store.ts). The Zustand store instantiates both engines and dispatches queries based on the current mode.
import { SearchEngine } from "@understand-anything/core/search";
import { SemanticSearchEngine } from "@understand-anything/core/embedding-search";
const searchEngine = new SearchEngine(nodes);
const semanticEngine = new SemanticSearchEngine(nodes, embeddings);
setSearchQuery: (query) => {
const mode = get().searchMode;
if (mode === "semantic" && semanticEngine.hasEmbeddings()) {
const queryEmbedding = /* obtained from external API */;
const results = semanticEngine.search(queryEmbedding, { limit: 20 });
set({ searchQuery: query, searchResults: results });
} else {
const results = searchEngine.search(query);
set({ searchQuery: query, searchResults: results });
}
}
When setSearchQuery is called, the store checks searchMode. If semantic mode is active and embeddings exist, it processes the query vector through SemanticSearchEngine; otherwise, it falls back to the Fuse-based SearchEngine.
Configuring and Switching Search Modes
Users toggle between strategies via the UI in [packages/dashboard/src/components/SearchBar.tsx](https://github.com/Lum1104/Understand-Anything/blob/main/understand-anything-plugin/packages/dashboard/src/components/SearchBar.tsx).
To programmatically switch modes:
// Switch to semantic search
store.setSearchMode('semantic');
// Switch back to fuzzy search
store.setSearchMode('fuzzy');
After setting the mode, subsequent calls to setSearchQuery automatically route to the appropriate engine. If semantic mode is selected but no embeddings are available, the system gracefully falls back to fuzzy search.
Implementation Details and Source Files
| File | Role | Implementation Link |
|---|---|---|
packages/core/src/search.ts |
Fuse-based fuzzy search with weighted field indexing | View source |
packages/core/src/embedding-search.ts |
Vector similarity search using cosine calculations | View source |
packages/dashboard/src/store.ts |
Zustand store holding search state and engine delegation logic | View source |
packages/dashboard/src/components/SearchBar.tsx |
UI component for input and mode toggle | View source |
packages/dashboard/src/components/KnowledgeGraphView.tsx |
Visualization component consuming search results | View source |
Summary
- Fuzzy search uses Fuse.js for approximate text matching across node names, tags, and summaries, supporting Boolean OR queries through pipe-separated input.
- Semantic search computes cosine similarity between query embeddings and pre-computed node vectors to surface conceptually related content regardless of exact keyword matches.
- The Zustand store in
store.tsacts as the central dispatcher, instantiating bothSearchEngineandSemanticSearchEngineand routing queries based on the activesearchMode. - If embeddings are unavailable in semantic mode, the dashboard automatically falls back to fuzzy search to ensure consistent functionality.
Frequently Asked Questions
What is the difference between fuzzy and semantic search in Understand-Anything?
Fuzzy search matches text based on approximate string similarity using Fuse.js, scoring results from 0 (perfect match) to 1 (worst match) with a threshold of 0.4. Semantic search uses vector embeddings and cosine similarity to find conceptually related nodes, converting similarity scores to Fuse-compatible metrics via 1 - similarity. Fuzzy search excels at keyword recall, while semantic search captures conceptual relationships even when terminology differs.
How does the dashboard handle search queries when no embeddings are available?
According to the implementation in store.ts, the dashboard checks semanticEngine.hasEmbeddings() before executing semantic search. If no embeddings exist or the check returns false, the store automatically routes the query to the fuzzy SearchEngine, ensuring users always receive results without manual intervention.
What fields does the fuzzy search engine index in Understand-Anything?
As implemented in packages/core/src/search.ts, the fuzzy search engine indexes four primary fields with varying weights: name (highest priority), tags, summary, and languageNotes (lowest priority). This weighting ensures that matches in node names appear higher in results than matches in supplementary metadata.
How is relevance scored in semantic search?
The SemanticSearchEngine calculates raw cosine similarity between the query vector and node vectors (0 to 1 scale), then converts these to Fuse-compatible scores using the formula 1 - similarity. This normalization allows semantic results to integrate seamlessly with the dashboard's existing result presentation layer, where lower scores indicate higher relevance.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →