How Fuzzy and Semantic Search Are Implemented in the Understand-Anything Dashboard

The Understand-Anything dashboard implements dual search modes—fuzzy keyword matching via Fuse.js and semantic vector similarity via cosine distance—unified through a common SearchResult interface managed by a Zustand store.

The Understand-Anything project provides an interactive dashboard for exploring codebases, where finding relevant nodes quickly requires robust search capabilities. This article examines how the repository implements both fuzzy and semantic search to accommodate exact keyword matching and conceptual similarity retrieval, ensuring developers can locate code regardless of terminology mismatches.

Fuzzy Search Implementation with Fuse.js

The fuzzy search engine relies on the Fuse.js library to perform weighted keyword matching against node metadata. Located in packages/core/src/search.ts, the SearchEngine class indexes graph nodes and supports extended search syntax for partial matches.

SearchEngine Configuration and Weights

The engine initializes with FUSE_OPTIONS that define searchable fields and their relevance weights. The configuration prioritizes node names (0.4), followed by tags (0.3), summaries (0.2), and language-specific notes (0.1), with a match threshold of 0.4.

// packages/core/src/search.ts
import Fuse, { type IFuseOptions } from "fuse.js";
import type { GraphNode } from "./types.js";

const FUSE_OPTIONS: IFuseOptions<GraphNode> = {
  keys: [
    { name: "name", weight: 0.4 },
    { name: "tags", weight: 0.3 },
    { name: "summary", weight: 0.2 },
    { name: "languageNotes", weight: 0.1 },
  ],
  threshold: 0.4,
  includeScore: true,
  ignoreLocation: true,
  useExtendedSearch: true,
};

export class SearchEngine {
  private fuse: Fuse<GraphNode>;

  constructor(nodes: GraphNode[]) {
    this.fuse = new Fuse(nodes, FUSE_OPTIONS);
  }

  search(query: string, options?: SearchOptions): SearchResult[] {
    const trimmed = query.trim();
    if (!trimmed) return [];

    const extendedQuery = trimmed.split(/\s+/).join(" | ");
    const rawResults = this.fuse.search(extendedQuery);
    /* …filter by type, slice by limit… */
    return rawResults.slice(0, limit).map(r => ({
      nodeId: r.item.id,
      score: r.score ?? 0,
    }));
  }
}

Extended Query Syntax

The search method transforms user input into an extended query format by joining terms with the | operator. This allows "auth contrl" to match entries containing either term, improving recall for imprecise inputs.

Semantic Search via Vector Embeddings

For conceptual retrieval, the semantic search implementation in packages/core/src/embedding-search.ts computes cosine similarity between query embeddings and pre-computed node embeddings. This enables finding semantically related content even without keyword overlap.

Cosine Similarity Calculation

The cosineSimilarity function calculates the dot product between two vectors normalized by their magnitudes, returning values between -1 and 1. The engine treats higher similarity as better matches, converting scores so that lower values indicate higher relevance (consistent with the fuzzy search scoring).

// packages/core/src/embedding-search.ts
import type { GraphNode } from "./types.js";

export function cosineSimilarity(a: number[], b: number[]): number {
  // dot product + magnitudes → cosine
  // returns 0 for zero-magnitude vectors
}

export class SemanticSearchEngine {
  private nodes: GraphNode[];
  private embeddings: Map<string, number[]>;

  constructor(nodes: GraphNode[], embeddings: Record<string, number[]>) {
    this.nodes = nodes;
    this.embeddings = new Map(Object.entries(embeddings));
  }

  search(queryEmbedding: number[], options?: SemanticSearchOptions): SearchResult[] {
    const limit = options?.limit ?? 10;
    const threshold = options?.threshold ?? 0;
    const typeFilter = options?.types;
    const scored: Array<{ nodeId: string; score: number }> = [];

    for (const node of this.nodes) {
      if (typeFilter && !typeFilter.includes(node.type)) continue;
      const embedding = this.embeddings.get(node.id);
      if (!embedding) continue;

      const similarity = cosineSimilarity(queryEmbedding, embedding);
      if (similarity >= threshold) {
        scored.push({ nodeId: node.id, score: 1 - similarity });
      }
    }

    scored.sort((a, b) => a.score - b.score);
    return scored.slice(0, limit);
  }
}

Type Filtering and Thresholds

The SemanticSearchEngine supports optional filtering by node type and minimum similarity thresholds. Results are sorted by ascending score (where 0 represents perfect similarity) and sliced according to the specified limit.

Dashboard Integration and State Management

The dashboard unifies both engines through a Zustand store defined in packages/dashboard/src/store.ts. The useDashboardStore hook manages the active search mode and routes queries to the appropriate engine.

Zustand Store Architecture

The store maintains searchMode state ("fuzzy" or "semantic") and instantiates a SearchEngine when the graph loads via setGraph. The setSearchQuery action delegates to the active engine's search method, normalizing results into the common SearchResult format.

// packages/dashboard/src/store.ts
export const useDashboardStore = create<DashboardStore>()((set, get) => ({
  searchMode: "fuzzy",
  setSearchMode: (mode) => set({ searchMode: mode }),

  setSearchQuery: (query) => {
    const engine = get().searchEngine;
    const mode = get().searchMode;
    
    if (!engine || !query.trim()) {
      set({ searchQuery: query, searchResults: [] });
      return;
    }
    
    // Line 528: SemanticSearchEngine will be used when mode is "semantic" 
    // and embeddings are present
    const searchResults = engine.search(query);
    set({ searchQuery: query, searchResults });
  },
}));

Runtime Mode Switching

When users toggle search modes through the UI, the setSearchMode action updates state immediately. While the current implementation uses the fuzzy engine for both modes, the architecture supports hot-swapping to SemanticSearchEngine when vector embeddings are available, as noted in the source comments.

Summary

  • Fuse.js powers fuzzy search in packages/core/src/search.ts, applying weighted field matching with a 0.4 threshold and extended query syntax.
  • Cosine similarity drives semantic search in packages/core/src/embedding-search.ts, comparing vector embeddings stored in a Map<string, number[]>.
  • Unified interface via SearchResult (nodeId + score) allows seamless UI rendering regardless of search mode.
  • Zustand store in packages/dashboard/src/store.ts orchestrates engine selection, query execution, and state persistence.
  • Lower scores indicate better matches in both engines, ensuring consistent result ranking across fuzzy and semantic modes.

Frequently Asked Questions

The project uses Fuse.js to handle fuzzy keyword matching. The SearchEngine class in packages/core/src/search.ts configures Fuse with field-specific weights for names, tags, summaries, and language notes, enabling tolerant matching of incomplete or misspelled queries.

How does semantic search calculate similarity between queries and nodes?

Semantic search computes cosine similarity between the query embedding and pre-computed node embeddings. Implemented in packages/core/src/embedding-search.ts, the cosineSimilarity function measures the angle between vectors, returning a value that the SemanticSearchEngine converts to a score where lower values represent higher relevance.

Can the dashboard filter search results by node type?

Yes, both search engines support type filtering. The fuzzy engine accepts a types array in its SearchOptions, while the SemanticSearchEngine checks options.types during iteration, skipping nodes that don't match the specified types before calculating similarity scores.

Where is the search mode state managed in the dashboard?

The Zustand store in packages/dashboard/src/store.ts manages the searchMode state ("fuzzy" or "semantic"). The setSearchMode action updates this state, while setSearchQuery routes execution to the currently active engine, with architecture prepared to instantiate SemanticSearchEngine when semantic mode and embeddings are both available.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →