Back to Projects

Narsil

Technology

Narsil is a distributed search engine that runs full-text, vector, hybrid, and geospatial search from one index.

Narsil: distributed search engine that embeds in your process or runs as a replicated search server

Narsil is an open-source distributed search engine. It partitions a large index across worker threads on its own, serialises each partition into a binary format that other languages can read, and merges the partial results into one ranked answer. It runs on Node.js, Bun, Deno, and the browser, and it is already in production, because the Cmd+K search on this site and on both project documentation sites is Narsil. The documentation site covers every feature, the source is on GitHub, and you can install the package from npm as @delali/narsil.

Four search modes in one engine

scoring supports field boosting, fuzzy matching through a bounded Levenshtein distance, and thresholds on both term coverage and score. A query composes with filters, facets, sorting, grouping, highlighting, cursor pagination, pinned results, and autocomplete suggestions.

Benchmark results

Narsil competes in two classes. Served over HTTP against Elasticsearch, OpenSearch, Qdrant, Weaviate, Typesense, and Meilisearch on the BEIR datasets, its BM25 takes the top on both, scoring 0.6814 on SciFact and 0.3278 on NFCorpus against 0.6789 and 0.3206 for Elasticsearch and OpenSearch. The margins are narrow, and Narsil holds them while answering more keyword queries per second than either engine, 958 against their 841 and 878 on SciFact and 1,089 against their 975 and 969 on NFCorpus. Embedded in one process, it takes the top nDCG@10 against Orama and MiniSearch. Its ranking reproduces the published Anserini baseline within 0.006 nDCG@10, and the full results come from a recorded continuous-integration run.

Partitioned indexes route documents by deterministic hash and reshape online through rebalance(), with writes buffering in a write-ahead queue during the reshape. Three scoring modes keep BM25 statistics consistent when an index spans partitions or instances. The HTTP server subpath wraps the engine in a REST API with health probes, bulk NDJSON import, and snapshot and restore endpoints, while events, typed errors, plugins, and memory reporting cover observability. Language modules cover 107 languages as separate entry points. Thirty-seven of those languages tokenise, stem, and filter stop words. Seven of them are written without spaces between words, so they split into character n-grams instead, and the remaining sixty-three tokenise and filter stop words.

Where this is going

Choosing a search engine today can force a team to trade power against how easily they can run it. Elasticsearch is powerful, though it takes real operational work to keep running. A lightweight library embeds easily, though it often struggles once a production workload arrives. Narsil is built to be both at once: distributed search you can embed in any runtime, in any language, with no separate cluster to manage.

The deeper ambition is language equity. Much of the search infrastructure in wide use was built around European languages, so a reader searching in a language that has no stemmer, tokeniser, or stopword list gets worse results than an English reader does for the same effort. Sixty-three of the 107 languages tokenise and filter stop words today but have no stemmer, because Snowball publishes none for them. I will build those stemmers next, with native speakers and computational linguists.

The goal reaches past any one language. One engine, one index format, and one query API should serve a browser tab, a single server process, and a cluster alike, in whichever language the application is written in. A team should be able to keep the engine it started with as its workload grows, and to have both the power and the ease of running it.

More in Technology

All projects

Search

Search for blog posts, studies, projects, and pages