# How Fuzzy and Semantic Search Work in the Understand Anything Dashboard: A Technical Deep Dive

> Explore fuzzy and semantic search in the Understand Anything dashboard. Learn how Fuse.js and cosine similarity power precise information retrieval for developers. Understand Anything repository technical deep dive.

- Repository: [Egonex/Understand-Anything](https://github.com/Egonex-AI/Understand-Anything)
- Tags: deep-dive
- Published: 2026-06-28

---

**The Understand Anything dashboard implements interchangeable search engines where fuzzy search uses Fuse.js for substring matching on node metadata, while semantic search computes cosine similarity between query embeddings and pre-computed node vectors, both managed through the `useDashboardStore` state container.**

The Understand Anything dashboard provides developers with dual search capabilities to navigate complex knowledge graphs efficiently. This article explains how the system implements both fuzzy and semantic search in the Understand Anything dashboard using the `SearchEngine` and `SemanticSearchEngine` classes located in the core package, wired into the dashboard's reactive state management.

## The Two Search Engines: Fuzzy vs. Semantic

The architecture centers on two distinct engines living in the **core** package that provide interchangeable search strategies. Both engines expose a uniform interface, allowing the UI to remain agnostic about which algorithm powers the search.

### Fuzzy Search with Fuse.js

The **fuzzy** engine is implemented in [`packages/core/src/search.ts`](https://github.com/Egonex-AI/Understand-Anything/blob/main/packages/core/src/search.ts) and leverages the **Fuse.js** library for approximate string matching. When the dashboard loads a graph, it instantiates a new `SearchEngine` with all graph nodes and predefined `FUSE_OPTIONS`.

The engine preprocesses queries into extended OR-style strings (e.g., converting "auth control" into `auth | control`) before passing them to Fuse. The scoring algorithm weights four specific node fields:

- **`name`** – The node identifier
- **`tags`** – Associated categorical labels
- **`summary`** – Brief description text
- **`languageNotes`** – Implementation-specific annotations

Results are returned as an array of `{nodeId, score}` objects, with lower scores indicating better matches.

### Semantic Search with Vector Embeddings

The **semantic** engine resides in [`packages/core/src/embedding-search.ts`](https://github.com/Egonex-AI/Understand-Anything/blob/main/packages/core/src/embedding-search.ts) and implements the `SemanticSearchEngine` class. This approach relies on pre-computed embedding vectors stored in a `Map<string, number[]>` where each key maps to a node identifier.

When a query string is converted into an embedding vector (typically via an external service like OpenAI), the engine calculates **cosine similarity** between the query vector and every node's stored vector using the `cosineSimilarity` function. The system converts similarity scores to distances using `1 - similarity`, sorts results by increasing distance, and returns the top matches passing an optional `threshold` parameter.

## Wiring the Engines to the Dashboard UI

The dashboard decouples search implementation from presentation through a centralized state management layer in [`packages/dashboard/src/store.ts`](https://github.com/Egonex-AI/Understand-Anything/blob/main/packages/dashboard/src/store.ts).

### State Management in the Dashboard Store

The `useDashboardStore` Zustand store maintains two critical properties for search management:

```typescript
searchMode: "fuzzy" | "semantic";
searchEngine: SearchEngine | null;

```

When a graph loads via the `setGraph` method, the store automatically instantiates a fuzzy `SearchEngine` with the graph nodes:

```typescript
// store.ts → setGraph
const searchEngine = new SearchEngine(graph.nodes);

```

Mode switching occurs through the `setSearchMode` action, which updates the state without recreating the engine. The actual query execution happens in `setSearchQuery`:

```typescript
// store.ts → setSearchQuery
const engine = get().searchEngine;
const mode   = get().searchMode;
if (!engine || !query.trim()) { … }

 // both modes currently use the same fuzzy engine
 // when embeddings are available, "semantic" will use SemanticSearchEngine
void mode;                       // placeholder for future branch
const searchResults = engine.search(query);
set({ searchQuery: query, searchResults });

```

According to the comment on line 28 of [`store.ts`](https://github.com/Egonex-AI/Understand-Anything/blob/main/store.ts), the current implementation falls back to the fuzzy engine for both modes, but the architecture is designed to seamlessly swap in `SemanticSearchEngine` once embeddings are available.

### Unified Result Interface

The UI components consume search results through the `searchResults` property of the store. Because both engines return a standardized `SearchResult[]` shape, the rendering layer requires no logic to distinguish between fuzzy substring matches and semantic vector similarity. Changing `searchMode` automatically routes queries through the appropriate algorithm without requiring UI modifications.

## Practical Implementation Examples

### Switching to Semantic Mode

Once embeddings are loaded, instantiate the semantic engine and update the store:

```typescript
import { useDashboardStore } from "./store";
import { SemanticSearchEngine } from "@understand-anything/core/embedding-search";

// Switch mode
useDashboardStore.getState().setSearchMode("semantic");

// Replace engine with semantic implementation
const { nodes } = useDashboardStore.getState().graph!;
const embeddings = await fetchEmbeddings(); // custom fetch
const semanticEngine = new SemanticSearchEngine(nodes, embeddings);
useDashboardStore.setState({ searchEngine: semanticEngine });

```

### Performing a Fuzzy Search

Execute a fuzzy query against node metadata:

```typescript
import { useDashboardStore } from "./store";

useDashboardStore.getState().setSearchMode("fuzzy");
useDashboardStore.getState().setSearchQuery("auth token");
const results = useDashboardStore.getState().searchResults;

// results → [{ nodeId: "node-123", score: 0.06 }, …]

```

### Computing Semantic Similarity

Search using vector embeddings with configurable thresholds:

```typescript
import { SemanticSearchEngine } from "@understand-anything/core/embedding-search";
import { embedQuery } from "./my-embedding-client";

async function semanticSearch(text: string) {
  const queryEmbedding = await embedQuery(text);
  const engine = new SemanticSearchEngine(
    useDashboardStore.getState().graph!.nodes,
    useDashboardStore.getState().graph!.embeddings
  );
  const results = engine.search(queryEmbedding, { limit: 10, threshold: 0.2 });
  console.log(results);
}

```

### Customizing Fuzzy Scoring Weights

Extend the base `SearchEngine` to modify Fuse.js configurations:

```typescript
import Fuse from "fuse.js";
import { SearchEngine } from "@understand-anything/core/search";

const customOptions = {
  keys: [{ name: "name", weight: 0.5 }, { name: "summary", weight: 0.5 }],
  threshold: 0.3,
  includeScore: true,
};

class MySearchEngine extends SearchEngine {
  constructor(nodes) {
    super(nodes);
    this.fuse = new Fuse(nodes, customOptions); // replace Fuse instance
  }
}

```

## Summary

- **Fuzzy search** in [`packages/core/src/search.ts`](https://github.com/Egonex-AI/Understand-Anything/blob/main/packages/core/src/search.ts) uses Fuse.js to match queries against the `name`, `tags`, `summary`, and `languageNotes` fields of graph nodes, preprocessing queries into OR-style patterns.
- **Semantic search** in [`packages/core/src/embedding-search.ts`](https://github.com/Egonex-AI/Understand-Anything/blob/main/packages/core/src/embedding-search.ts) implements `SemanticSearchEngine` to compute cosine similarity between query embeddings and pre-computed node vectors stored in a `Map<string, number[]>`.
- The `useDashboardStore` in [`packages/dashboard/src/store.ts`](https://github.com/Egonex-AI/Understand-Anything/blob/main/packages/dashboard/src/store.ts) manages the active search mode and engine instance, allowing seamless switching between algorithms without UI changes.
- Both engines return a uniform `SearchResult[]` interface, decoupling the search implementation from the dashboard presentation layer.
- The architecture currently defaults to fuzzy matching for both modes but is designed to support full semantic search once embeddings are populated.

## Frequently Asked Questions

### What is the difference between fuzzy and semantic search in the Understand Anything dashboard?

Fuzzy search performs approximate string matching on node metadata using Fuse.js, scoring results based on text similarity in fields like `name` and `summary`. Semantic search converts queries and node content into vector embeddings, then ranks results by cosine similarity to find conceptually related content regardless of exact keyword matches.

### How does the dashboard switch between search modes?

The `useDashboardStore` maintains a `searchMode` state that can be toggled between `"fuzzy"` and `"semantic"` using the `setSearchMode` method. The store's `searchEngine` property holds the active instance, and while the current implementation uses the fuzzy engine for both modes, the architecture supports swapping in `SemanticSearchEngine` when embeddings are available.

### Where are the search embeddings stored in the Understand Anything architecture?

Pre-computed embeddings are stored in a `Map<string, number[]>` within the graph data structure, where each key corresponds to a node identifier. The `SemanticSearchEngine` in [`packages/core/src/embedding-search.ts`](https://github.com/Egonex-AI/Understand-Anything/blob/main/packages/core/src/embedding-search.ts) consumes this map to perform vector similarity calculations against query embeddings.

### Can I customize the fuzzy search scoring weights?

Yes, the `SearchEngine` class can be extended to override the default Fuse.js configuration. By creating a subclass and replacing the `this.fuse` instance with custom `FUSE_OPTIONS`, you can adjust field weights, threshold sensitivity, and other Fuse.js parameters to tune search relevance for your specific dataset.