Fuse.js

repository·main·Indexed 10 days ago

https://github.com/krisk/fuse

A lightweight, zero-dependency fuzzy-search library for searching small-to-medium datasets in the browser or on the server. Version 7.6.0-beta.0 features typo tolerance via the Bitap algorithm, weighted keys, multi-word token search, and logical search. It offers both Basic and Full builds to balance functionality and bundle size, and includes FuseWorker for parallel searching of large datasets using Web Workers.

Tokens
24.3K
Snippets
85
Records
112
Agent score
97%

What's inside Fuse.js

  1. Overview of Fuse.js features

    main

    Fuse.js is a lightweight, zero-dependency fuzzy-search library compatible with browsers, Node.js, and Deno. Key capabilities include:

    • Fuzzy search: Typo-tolerant matching using the Bitap algorithm.
    • Token search: Multi-word query splitting with term-based fuzzy matching and IDF ranking.
    • Extended search: Support for operators like exact, prefix, suffix, inverse, and include matching.
    • Logical search: Structured queries using $and and $or expressions.
    • Weighted keys: Ability to boost specific fields (e.g., prioritizing title over description).
    • Nested search: Accessing data via dot notation, array notation, or a custom getFn.
    • Two builds: Available in a full version (~8.6 kB gzip) or a basic version (~6.8 kB gzip).
  2. Control fuzziness using threshold, location, and distance

    main

    You can fine-tune how strictly Fuse.js matches patterns using three primary interacting options:

    • threshold: The cutoff for the fuzziness score. 0.0 requires a perfect match; 1.0 matches anything. (Default: 0.6)
    • location: The expected starting position of the pattern in the text. (Default: 0)
    • distance: How far from location a match can be before being penalized. The effective search window is threshold × distance.

    Important Note on Long Text: With default settings (threshold: 0.6, distance: 100), Fuse.js effectively only searches the first ~60 characters of a field. If your data contains long text fields, you should either increase distance or set ignoreLocation: true.

    // Example: Searching long text by ignoring location
    const fuse = new Fuse(data, {
      threshold: 0.6,
      ignoreLocation: true
    });
  3. Compose complex queries with Object Syntax

    main

    Object queries can be composed within a single field or across multiple fields using logical operators.

    Within a single field

    • Multiple operators in one object are ANDed.
    • Use $or: [...] for OR groups.
    • Use $and: [...] to repeat the same operator (since JS object keys must be unique).

    Across multiple fields

    Nest object queries within logical query operators like $and and $or to build deep search trees.

    Example of deep composition:

    // category is 'fiction' AND (title starts with 'old' OR author contains 'scalzi')
    fuse.search({
      $and: [
        { category: { $eq: 'fiction' } },
        {
          $or: [
            { title: { $startsWith: 'old' } },
            { author: { $contains: 'scalzi' } }
          ]
        }
      ]
    })
    // starts with "old" AND ends with "war"
    fuse.search({ title: { $startsWith: 'old', $endsWith: 'war' } })
    
    // starts with "old" OR ends with "war"
    fuse.search({ title: { $or: [{ $startsWith: 'old' }, { $endsWith: 'war' }] } })
    
    // fuzzy "old" AND fuzzy "war"
    fuse.search({ title: { $and: [{ $fuzzy: 'old' }, { $fuzzy: 'war' }] } })
  4. Understand and customize scoring

    main

    The final relevance score (from 0 to 1) is a combination of the fuzziness score, the key weight, and the field-length norm (shorter fields rank higher). To see these scores, you must set includeScore: true in your configuration.

    You can adjust how the field-length norm affects the score using:

    • ignoreFieldNorm: When true, field length has no effect on scoring. (Default: false)
    • fieldNormWeight: Adjusts the strength of the field-length norm. 0 ignores it, 0.5 reduces it, and 2.0 amplifies it. (Default: 1)
  5. Use implicit AND for multiple expressions

    main
    When specifying a comma-separated list of expressions within an object, Fuse.js performs an implicit AND. You only need to use the explicit $and operator when the same field or operator appears in multiple expressions within the query structure.
  6. How Fuse.js handles typos via Bitap

    main

    Fuse.js implements fuzzy search using the Bitap algorithm. Instead of a single state vector for exact matching, it maintains multiple state vectors representing different error levels:

    • R0: Zero errors (exact match).
    • R1: One error allowed.
    • R2: Two errors allowed, and so on.

    When a character mismatch occurs, the match 'dies' in the current error level but is 'carried over' to the next level (e.g., from R0 to R1), counting the mismatch as an error. This allows the engine to handle:

    • Substitutions: Replacing a character.
    • Deletions: Missing a character from the pattern.
    • Insertions: An extra character in the text.

    The algorithm continues adding error levels until the resulting score exceeds the configured threshold.

  7. When to use Fuse.js vs Semantic Search

    main

    When building search features, you must choose between character-based fuzzy search (Fuse.js) and meaning-based semantic search (embeddings).

    Use Fuse.js when:

    • Users are typing to find something they already know exists (e.g., names, titles, SKUs, settings, commands).
    • The dataset is relatively small (under ~100k items) and can fit in the browser memory.
    • You require zero latency (< 10ms) and zero infrastructure cost.
    • The application needs to work offline or requires high privacy (data stays on device).
    • Typo tolerance is a priority.

    Use Semantic Search when:

    • Users are describing what they want in natural language (e.g., "Find articles about climate change").
    • You need to find items based on meaning rather than exact character matches.
    • You are building a RAG (Retrieval-Augmented Generation) pipeline for an LLM.
    • The dataset is massive (millions of documents) and requires a server-side vector database.
    • You need cross-language search capabilities.

    Comparison Summary

    FeatureFuse.jsSemantic Search
    MatchingCharacter similarity (fuzzy)Semantic similarity (meaning)
    Runs onClient (browser/Node)Server (API + database)
    Latency< 10ms50–500ms
    CostFreeEmbedding API + DB hosting
    Handles typosYesPoorly
    OfflineYesNo
    Dataset sizeThousands to ~100kMillions+
  8. Combine Fuse.js and Semantic Search in one application

    main

    For advanced AI applications, use a layered approach:

    1. Fuse.js for the fast, interactive 'search-as-you-type' layer (finding known pages, titles, or products).
    2. Semantic Search for the natural language layer (answering complex questions or finding conceptually similar content).

    Example Pattern (Documentation Site):

    • Use Fuse.js to instantly filter documentation titles and headings as the user types.
    • Use Semantic Search (Embeddings + Vector DB) when the user asks a specific question to retrieve relevant context for an LLM.

    Example Pattern (E-commerce):

    • Use Fuse.js for the product search bar (typo-tolerant filtering).
    • Use Semantic Search for "Find similar products" recommendations or natural language queries like "warm jacket under $100".
  9. Use Extended Search with string operators

    main

    Enable precise control over matches by setting useExtendedSearch: true. This allows you to use special characters in your query strings to perform exact matches, prefix searches, or exclusions.

    const fuse = new Fuse(list, {
      useExtendedSearch: true,
      keys: ['title']
    })
    
    fuse.search('=exact match') // exact match
    fuse.search('^prefix')     // starts with
    fuse.search('!term')       // does not include
  10. When to use FuseWorker vs Fuse

    main

    Choosing between the standard Fuse class and FuseWorker depends on your dataset size and UI requirements:

    • Use Fuse (Standard) if:
      • Your dataset is small (< 5K items).
      • You need features unsupported by FuseWorker (e.g., sortFn, getFn, useTokenSearch, or remove()).
      • You are running in a Node.js environment.
    • Use FuseWorker if:
      • Your dataset is large (10K+ items).
      • You need to maintain a responsive UI (no frame drops) during search-as-you-type.
      • You are targeting mobile devices with slower CPUs.

    Performance Note: For datasets between 5K and 10K items, both are viable; test with your specific data.

  11. How Fuse Cloud works

    main

    Fuse Cloud is a hosted search service that follows a three-step workflow:

    1. Upload your data: Push a JSON array (the same format used with local Fuse.js) to the service.
    2. Get a search API: Fuse Cloud indexes your data and provides you with a publicKey.
    3. Search from the client: Call the API directly from your frontend using the @fusejs/cloud client. No custom backend is required.
  12. Understand the fuzziness score and threshold

    main

    Fuse.js uses a fuzziness score to rank results, where:

    • 0 is a perfect match (zero edit distance).
    • 1 is a complete mismatch.

    The final score is calculated by combining the edit distance with other factors like key weight and field-length normalization.

    You can control how strict or loose the search is using the threshold option. The threshold acts as a cutoff: any result with a score higher than the threshold is excluded from the results.

    • Lower threshold: More strict (requires closer matches, fewer errors allowed).
    • Higher threshold: More loose (allows more typos and approximate matches).

    The default threshold is 0.6.