# How Semantic and Fuzzy Search Work in the Understand-Anything Dashboard

> Discover how the Understand Anything dashboard uses fuzzy and semantic search to find what you need. Learn about Fuse.js and vector similarity with this insightful guide from Lum1104.

- Repository: [Yuxiang Lin/Understand-Anything](https://github.com/Lum1104/Understand-Anything)
- Tags: how-to-guide
- Published: 2026-06-01

---

**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](https://github.com/Lum1104/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/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`, and `languageNotes` with 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/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 `cosineSimilarity` helper function.
- **Score conversion**: Raw similarity scores (0 to 1) are converted to Fuse-compatible scores via `1 - similarity` to 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/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.

```typescript
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/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:

```typescript
// 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`](https://github.com/Lum1104/Understand-Anything/blob/main/packages/core/src/search.ts) | Fuse-based fuzzy search with weighted field indexing | [View source](https://github.com/Lum1104/Understand-Anything/blob/main/understand-anything-plugin/packages/core/src/search.ts) |
| [`packages/core/src/embedding-search.ts`](https://github.com/Lum1104/Understand-Anything/blob/main/packages/core/src/embedding-search.ts) | Vector similarity search using cosine calculations | [View source](https://github.com/Lum1104/Understand-Anything/blob/main/understand-anything-plugin/packages/core/src/embedding-search.ts) |
| [`packages/dashboard/src/store.ts`](https://github.com/Lum1104/Understand-Anything/blob/main/packages/dashboard/src/store.ts) | Zustand store holding search state and engine delegation logic | [View source](https://github.com/Lum1104/Understand-Anything/blob/main/understand-anything-plugin/packages/dashboard/src/store.ts) |
| [`packages/dashboard/src/components/SearchBar.tsx`](https://github.com/Lum1104/Understand-Anything/blob/main/packages/dashboard/src/components/SearchBar.tsx) | UI component for input and mode toggle | [View source](https://github.com/Lum1104/Understand-Anything/blob/main/understand-anything-plugin/packages/dashboard/src/components/SearchBar.tsx) |
| [`packages/dashboard/src/components/KnowledgeGraphView.tsx`](https://github.com/Lum1104/Understand-Anything/blob/main/packages/dashboard/src/components/KnowledgeGraphView.tsx) | Visualization component consuming search results | [View source](https://github.com/Lum1104/Understand-Anything/blob/main/understand-anything-plugin/packages/dashboard/src/components/KnowledgeGraphView.tsx) |

## 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.ts`](https://github.com/Lum1104/Understand-Anything/blob/main/store.ts) acts as the central dispatcher, instantiating both `SearchEngine` and `SemanticSearchEngine` and routing queries based on the active `searchMode`.
- 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`](https://github.com/Lum1104/Understand-Anything/blob/main/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`](https://github.com/Lum1104/Understand-Anything/blob/main/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.