microfuzz

repository·main·Indexed 42 days ago

https://github.com/nozbe/microfuzz

A tiny (2KB gzipped), fast, and framework-agnostic JavaScript fuzzy search library for short lists of items. It features case-insensitive and diacritics-insensitive matching with 'smart', 'aggressive', and 'off' strategies. Includes a React/React Native integration providing the `useFuzzySearchList` hook and a `Highlight` component for rendering matching character ranges.

Tokens
5.6K
Snippets
18
Records
23
Agent score
53%

What's inside microfuzz

  1. How microfuzz works: Core Concepts

    main

    microfuzz is a lightweight, in-memory fuzzy search library designed for speed and simplicity.

    Key Characteristics:

    • Search Logic: Case-insensitive and diacritics-insensitive. It matches query letters in order (but not necessarily consecutively) and supports matching query words in any order. It works with Latin, Cyrillic, and rudimentary CJK scripts.
    • Scoring: Sorts results based on how well the text matches the query using simple heuristics. If scores are equal, the original input order is preserved.
    • Output: Returns matching items along with character ranges (matches) used for UI highlighting.
    • Limitations: It is not a full-text search engine. It does not include stemming, soundex, Levenshtein distance, or autocorrect. It is intended for short strings like labels, names, or task titles rather than long-form text like notes or comments.
  2. Publish microfuzz normally

    main

    To publish a stable version of the package to npm, follow these steps:

    1. Verify code integrity and build the project:
      yarn ci:check
      yarn build
    2. Increment the version number:
       ```bash
    npm version <version-number>
    1. Publish the package:
      npm publish
    4. Push the version bump and tags to your remote repository:
       ```bash
    git push && git push --tags
    yarn ci:check
    yarn build
    npm version 1.1.1
    npm publish
    git push && git push --tags
  3. Update documentation on GitHub Pages

    main

    To update the hosted documentation (GitHub Pages) using the demo folder, follow this workflow:

    1. Switch to the documentation branch: git checkout gh-pages.
    2. Merge the latest changes from the main branch: git merge main.
    3. Navigate to the demo directory and deploy: cd demo && yarn gh-pages.
    4. Commit and push the updated documentation.
    git checkout gh-pages
    git merge main
    cd demo && yarn gh-pages
  4. Publish microfuzz as a pre-release

    main

    To publish a pre-release version (e.g., for testing or beta distribution), use npm publish --tag next. This ensures that users installing the package via npm install @nozbe/microfuzz will not accidentally receive the pre-release version unless they explicitly request the next tag.

    Workflow:

    1. Run checks and build: yarn ci:check and yarn build.
    2. Bump version: npm version <version-number> (e.g., 1.1.1-1).
    3. Publish: npm publish --tag next.
    4. Push changes: git push && git push --tags.
    yarn ci:check
    yarn build
    npm version 1.1.1-1
    npm publish --tag next
    git push && git push --tags
  5. Use microfuzz in React or React Native

    main

    For React and React Native applications, @nozbe/microfuzz/react provides hooks and components to simplify integration:

    • useFuzzySearchList: A hook that wraps createFuzzySearch with built-in memoization. It returns a filtered list of items.
      • Performance Tip: You should memoize the getText and mapResultItem functions passed to this hook to prevent unnecessary re-renders.
    • Highlight: A component used to render text with matching character ranges highlighted.

    The mapResultItem option in useFuzzySearchList allows you to transform the FuzzyResult<T> (which contains the item, score, and matches) into the shape your UI expects.

    import { useFuzzySearchList, Highlight } from '@nozbe/microfuzz/react'
    
    // `useFuzzySearchList` simply wraps `createFuzzySearch` with memoization built in
    // NOTE: For best performance, `getText` and `mapResultItem` should be memoized by user
    const filteredList = useFuzzySearchList({
      list,
      // If `queryText` is blank, `list` is returned in whole
      queryText,
      // optional `getText` or `key`, same as with `createFuzzySearch`
      getText: (item) => [item.name],
      // arbitrary mapping function, takes `FuzzyResult<T>` as input
      mapResultItem: ({ item, score, matches: [highlightRanges] }) => ({ item, highlightRanges })
    })
    
    // Render `filteredList`'s labels with matching characters highlighted
    filteredList.map(({ item, highlightRanges }) => (
      <Item key={item.key}>
        <Label><Highlight text={item.name} ranges={highlightRanges} /></Label>
      </Item>
    ))
  6. Use microfuzz in plain JavaScript

    main

    To use microfuzz in a standard JavaScript environment, import createFuzzySearch from @nozbe/microfuzz. The process is split into two steps for performance: first, initialize the searcher with your list (this pre-processes the data), and then call the returned function with your query text.

    If you are searching an array of objects, you can specify which property to search using the key option, or provide a getText function to extract one or more strings from each object for searching.

    import createFuzzySearch from '@nozbe/microfuzz'
    
    const list = [/* an array of strings to fuzzy search */]
    const fuzzySearch = createFuzzySearch(list)
    
    // Run this whenever search term changes
    const results = fuzzySearch(queryText)
  7. Understand FuzzyResult and sorting behavior

    main

    A FuzzyResult<T> contains the original item, a score, and matches (highlighting ranges).

    Scoring and Sorting

    Results are sorted by score in ascending order (lower is better). The internal scoring hierarchy is roughly:

    1. Exact match
    2. Full match (case/diacritic insensitive)
    3. "Starts with" match
    4. Contains query (at word boundary) exactly
    5. Contains query (at word boundary)
    6. Contains query words (space separated) in any order
    7. Contains query
    8. Contains letters (fewer chunks are better)

    Note: Exact scoring values are not an API contract and may change between releases.

    Match Types

    • Range: [index of first character, index of last character]
    • HighlightRanges: Range[]
    • FuzzyMatches: Array<HighlightRanges | null> (usually one set of ranges per string, but can be multiple if getText returned multiple strings).
  8. Configure search keys and text extraction in microfuzz

    main

    When searching an array of objects, use the options object in createFuzzySearch to define how items are evaluated:

    • key: A string representing the property name to search.
    • getText: A function that takes an item and returns an array of strings to be used for the search. This allows searching across multiple properties or computed values.

    Note: For optimal performance, the function returned by createFuzzySearch should be cached or memoized.

    const fuzzySearch = createFuzzySearch(list, {
      // search by `name` property
      key: 'name',
      // search by `description.text` property
      getText: (item) => [item.description.text],
      // search by multiple properties:
      getText: (item) => [item.name, item.description.text]
    })
  9. Configure FuzzySearchOptions

    main

    When calling createFuzzySearch, you can provide an options object to control how items are processed:

    • key: A string representing a property name on the objects in the list to search against.
    • getText: A function (item: unknown) => Array<string | null> used to extract searchable text. This allows searching across multiple properties or custom logic.
    • strategy: A FuzzySearchStrategy ('off', 'smart', or 'aggressive') determining the matching strictness.
    // Search by a specific property
    createFuzzySearch(list, { key: 'text' })
    
    // Search by multiple properties using getText
    createFuzzySearch(list, { 
      getText: (item) => [item.text, item.otherText] 
    })
  10. Configure fuzzy search strategies

    main

    You can control the strictness of the fuzzy matching by passing a strategy option to createFuzzySearch or useFuzzySearchList. Available strategies are:

    • 'off': Disables fuzzy matching. Only matches if the item contains the query (or query words in any order).
    • 'smart': The default strategy. Matches letters in order, but rejects poor-quality matches.
    • 'aggressive': Classic fuzzy search. Matches letters in order with no restrictions on match quality.
    // Example usage of strategy
    const fuzzySearch = createFuzzySearch(list, { strategy: 'aggressive' });
  11. Create a fuzzy searcher with createFuzzySearch

    main

    Use createFuzzySearch to generate a search function optimized for a specific list of items. The returned function takes a queryText string and returns an array of FuzzyResult<T> objects, sorted by match quality.

    If the list is an array of strings, it can be searched directly. For arrays of objects, use the options parameter to specify how to extract searchable text via key or getText.

    Search Strategies

    • 'off': No fuzzy matching; only exact contains/starts-with matches.
    • 'smart' (default): Matches letters in order, but ignores poor-quality matches.
    • 'aggressive': Classic fuzzy search; matches letters in order with no restrictions.
    const fuzzySearch = createFuzzySearch(list, { strategy: 'smart' })
    const results = fuzzySearch(queryText)