altor-vec vs lunr.js

altor-vec vs lunr.js - Semantic Search vs Keyword Search

What is the difference between altor-vec and lunr.js?

lunr.js uses TF-IDF full-text indexing. It finds documents containing the words you type. altor-vec uses HNSW vector search. It finds documents whose meaning is close to your query, even when different words are used. A search for "cancel subscription" finds "end your plan" in altor-vec but not in lunr.js.

If you are evaluating a lunr.js alternative, the core decision is keyword search versus semantic search. lunr.js builds an inverted index and scores documents with TF-IDF, which works extremely well when users search with the same vocabulary that appears in your content. altor-vec uses vector embeddings and HNSW retrieval, so it can match intent and paraphrase instead of only matching exact terms. For docs, changelogs, and static sites where keywords are known, lunr.js is a classic choice. For product search, support portals, and larger knowledge bases where users phrase things differently, vector search is usually the better fit.

Install altor-vec: npm install altor-vec

Feature comparison

Capability altor-vec lunr.js
Algorithm HNSW vector search over pre-built embeddings Inverted index with TF-IDF scoring
Semantic understanding Yes. Finds meaning, paraphrase, and related intent No. Matches literal terms in the index
Bundle size 54KB WebAssembly bundle About 8KB minified
Setup complexity Moderate. Requires embeddings and a generated binary index Very low. Index JavaScript objects directly
Works offline Yes Yes
Typo handling Depends on embedding quality and preprocessing, not a dedicated fuzzy matcher Basic fuzzy term syntax, but still keyword-based
Build step required Yes No for basic usage
License MIT MIT

lunr.js mindset

Index fields, tokenize text, and rank matches by term frequency. This model is fast, predictable, tiny, and ideal when users search for the same words that appear in your content. It is why lunr.js has stayed popular on static docs sites since 2013.

altor-vec mindset

Pre-compute embeddings, build an HNSW index, and retrieve the nearest results by meaning. This approach costs more upfront, but it handles paraphrase, vague wording, and natural language queries that traditional full-text search misses.

Why teams migrate

Teams usually outgrow lunr.js when exact keywords stop being enough. Product help centers, policy libraries, internal knowledge bases, and support search all benefit when users can search in their own words and still reach the right answer.

Code comparison

These two examples show the tradeoff clearly. lunr.js is easier to start with. altor-vec needs a pre-built index, but it solves the query understanding problem that keyword search cannot.

altor-vec

import { Index } from 'altor-vec';

const index = await Index.load('/search/help-center.bin');

const results = await index.search('cancel subscription', {
  limit: 5,
});

console.log(results[0]);
// Can return a document like: "How to end your plan"

lunr.js

import lunr from 'lunr';

const idx = lunr(function () {
  this.ref('id');
  this.field('title');
  this.field('content');
  docs.forEach(doc => this.add(doc));
});

// Will NOT find "end your plan" when searching "cancel subscription"
const results = idx.search('cancel subscription');

When to choose each

Choose lunr.js if

  • You want the smallest possible client-side search library.
  • You are building docs, blogs, or static sites where users know the exact terminology.
  • You want zero dependencies and near-zero setup.
  • You do not want an embedding pipeline or build-time indexing job.
  • Your content set is small enough that keyword search is already good enough.

Choose altor-vec if

  • You need semantic understanding instead of literal keyword matching.
  • You want queries like "cancel subscription" to find "end your plan".
  • Your dataset is growing and search quality matters more than raw bundle size.
  • You are indexing support content, product help, internal knowledge, or broad content corpora.
  • You can afford a build step to generate embeddings and ship a binary index.

Upgrading from lunr.js

  1. Audit your search logs and identify queries that fail because the right answer uses different wording than the query.
  2. Export the documents you currently index in lunr.js, including titles, body text, URLs, and any metadata you want to return in results.
  3. Generate embeddings for those documents during your build process and create an altor-vec binary index.
  4. Load the generated .bin file in the browser and replace idx.search(...) calls with semantic vector queries.
  5. Keep your result rendering UI, then compare offline relevance for real queries such as synonyms, paraphrases, and task-oriented searches.
  6. Roll out altor-vec where semantic retrieval matters most, while keeping lunr.js in places where keyword precision and tiny bundle size still win.

FAQ

Is altor-vec better than lunr.js?

It depends on what you need. lunr.js is better for keyword-based full-text search where terms in the query match terms in the document. altor-vec is better when you need semantic understanding, such as finding "end your plan" when a user searches "cancel subscription". If your use case is a docs search where users know the vocabulary, lunr.js is simpler and smaller. If meaning matters more than literal words, altor-vec is the upgrade.

Can lunr.js understand synonyms or paraphrases?

No. lunr.js uses TF-IDF scoring over an inverted index. It can only match queries against terms that literally appear in the indexed documents. Synonyms, paraphrases, and intent-level queries require a separate synonym list or a semantic search engine like altor-vec.

How do altor-vec and lunr.js compare in bundle size?

lunr.js is approximately 8KB minified. altor-vec ships as a 54KB WebAssembly bundle. The extra size buys you semantic retrieval, which is meaning-based search that TF-IDF cannot replicate. For tiny datasets or keyword-only use cases, lunr.js is the more efficient choice.

Does altor-vec work on static sites like lunr.js?

Yes. Both libraries are fully client-side and work on static sites with no server required. The difference is that lunr.js builds its index from JSON at page load time, while altor-vec loads a pre-built binary index file generated at build time. Both result in offline-capable search with no backend.

Which is easier to set up: altor-vec or lunr.js?

lunr.js is significantly easier to set up. You can index an array of JavaScript objects with a few lines of code and no embedding pipeline. altor-vec requires a build step to generate embeddings for your documents and serialize the HNSW index to a binary file. That complexity is the price of semantic search capability.

Bottom line

lunr.js remains one of the best tools for lightweight client-side keyword search. It is tiny, dependable, offline-friendly, and ideal for docs search where exact words matter. altor-vec is the stronger choice when users search by intent instead of exact phrasing. If you are looking for a lunr.js alternative because relevance breaks down on real language queries, vector search is the reason to switch.

Try altor-vec: npm install altor-vec ยท View the GitHub repository