Files
skills/plugins/library-navigator/skills/sp-search/SKILL.md
2026-05-29 08:20:56 +01:00

3.5 KiB

name, description
name description
sp-search Full-text and fuzzy search primitives with in-memory BM25 engine, trigram fuzzy matching, faceted search, and pagination support.

Architecture

Component Responsibility
createMemorySearchEngine / MemorySearchEngine In-memory BM25 search engine with indexing, search, faceting, and pagination
tokenize / normalizeToken / simpleStem Text tokenization pipeline with stemming and normalisation
buildTrigrams / trigramSimilarity Trigram-based fuzzy string similarity scoring
bm25Score / computeIdf / tfIdfScore BM25 and TF-IDF scoring functions for relevance ranking
SEARCH_CODES + SearchError Structured error registry for query validation and engine failures
FieldConfigSchema / SearchQuerySchema / PaginationSpecSchema Zod schemas for all query and configuration types

File Structure

packages/persistence/search/search/src/
  engine/memory-engine/   MemorySearchEngine and createMemorySearchEngine
  text/tokenizer/         tokenize, normalizeToken, simpleStem
  text/trigram/           buildTrigrams, trigramSimilarity
  text/scorer/            bm25Score, computeIdf, tfIdfScore
  errors/                 SearchError and SEARCH_CODES
  schemas/                Zod schemas for queries, filters, pagination, fields
  types/                  TypeScript interfaces (SearchDocument, SearchHit, FacetBucket, etc.)
  constants/              BM25_B, BM25_K1, DEFAULT_FUZZY_THRESHOLD, DEFAULT_PAGE_SIZE

Sibling packages

  • @suaveplan/pagination — pagination primitives consumed by search results
  • @suaveplan/error — structured error registry via defineCodes
  • @suaveplan/types — shared TypeScript primitives
  • @suaveplan/testing — unit test infrastructure

Patterns

Quick Start

import { createMemorySearchEngine } from "@suaveplan/search";

interface Product { id: string; name: string; price: number }

const engine = createMemorySearchEngine<Product>({
  fields: [
    { name: "name", type: "text", searchable: true },
    { name: "price", type: "number", sortable: true },
  ],
});

await engine.index([
  { id: "1", name: "Laptop Pro", price: 999 },
  { id: "2", name: "Laptop Air", price: 799 },
]);

const result = await engine.search({ text: "laptop" });
console.log(result.hits.length); // 2

Fuzzy matching with trigrams

import { trigramSimilarity, buildTrigrams } from "@suaveplan/search/text/trigram/trigram";

const score = trigramSimilarity("colour", "color");

BM25 scoring

import { bm25Score, computeIdf } from "@suaveplan/search/text/scorer/scorer";

const idf = computeIdf(totalDocs, docsContainingTerm);
const score = bm25Score({ tf: 3, docLength: 100, avgDocLength: 80, idf });

Constraints

  • In-memory only; no persistent backing store
  • BM25 parameters BM25_K1 and BM25_B are exported constants (default 1.2 and 0.75)
  • Fuzzy threshold defaults to DEFAULT_FUZZY_THRESHOLD; override via search options
  • Pagination uses @suaveplan/pagination primitives
  • OTel instrumentation via @opentelemetry/api (no-op when no SDK registered)

Checklist

  • Verify MemorySearchEngine supports text, number, and boolean field types
  • Confirm SEARCH_CODES registered via defineCodes("SEARCH", [...])
  • Check subpath exports in package.json align with src/ modules
  • Run bunx turbo lint typecheck test build --filter=@suaveplan/search
  • Confirm benchmarks in src/__benchmarks__/ execute without error