migration guide
Migrate from Algolia to altor-vec
Migrate if you're paying Algolia for a documentation or content-search use case where queries are natural-language phrases and your index is under a few hundred megabytes. Stay on Algolia if you need typo tolerance, faceted filtering, real-time re-indexing under 100 ms, or A/B testing — those are Algolia's genuine strengths and altor-vec does not replicate them.
When migration makes sense
- Your Algolia bill is growing faster than your product. Once you cross the free tier, costs compound with every search. A documentation site with 50,000 monthly visitors can easily reach $300–500/month. altor-vec eliminates the per-query cost entirely.
- You ship a static site or Jamstack app. altor-vec is a browser-native WASM module with no server round-trip. If your site is already served from a CDN, semantic search can run without adding any backend infrastructure.
- Your queries are natural-language, not keyword lookups. Users asking "how do I revoke an API token" will find the right page even if the docs say "invalidate credentials" — vector search bridges vocabulary gaps that Algolia's keyword engine requires you to solve with synonyms.
- You want data to stay client-side. Algolia's servers receive every query your users type. altor-vec runs inference entirely in the browser — nothing leaves the device at query time.
What you give up
Be honest with your team before starting. altor-vec cannot replace these Algolia capabilities without significant custom work:
- Typo tolerance. Algolia corrects "recieve" → "receive" and "htlm" → "html" automatically. altor-vec's embedding model is somewhat robust to minor misspellings (the vector space is fuzzy), but it will miss aggressive typos that change the phonetic shape of a word.
- Faceted search with real-time counts. Algolia's facet system computes category counts dynamically against your full index. altor-vec has no facet API — you would need to implement client-side grouping after retrieving results.
- Instant re-indexing. Algolia updates appear in search within seconds of an API push. altor-vec requires you to rebuild and redeploy your static index file whenever content changes. For frequently-updated catalogs (e.g., live inventory), this rebuild cycle adds latency.
- A/B testing and analytics. Algolia's dashboard provides click-through rates, zero-result queries, and search analytics out of the box. altor-vec is search-only; you'd need to instrument your own analytics layer.
Step-by-step migration
Step 1 — Export all records from Algolia
Use the Algolia JavaScript client to page through your index and download every object. The browseObjects method handles pagination automatically.
// export-algolia.mjs (run in Node.js)
import algoliasearch from 'algoliasearch';
import { writeFileSync } from 'fs';
const client = algoliasearch('YOUR_APP_ID', 'YOUR_ADMIN_API_KEY');
const index = client.initIndex('YOUR_INDEX_NAME');
const records = [];
await index.browseObjects({
query: '',
attributesToRetrieve: ['objectID', 'title', 'content', 'url'],
batch: (batch) => records.push(...batch),
});
writeFileSync('algolia-export.json', JSON.stringify(records, null, 2));
console.log(`Exported ${records.length} records`);
Step 2 — Generate embeddings for each record
Concatenate the fields that carry search meaning. For documentation, title + content works well. Use any embedding API you prefer — the example below uses the OpenAI embeddings endpoint.
// embed-records.mjs
import { readFileSync, writeFileSync } from 'fs';
import OpenAI from 'openai';
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const records = JSON.parse(readFileSync('algolia-export.json', 'utf8'));
const BATCH = 100; // embed in batches to respect rate limits
const embedded = [];
for (let i = 0; i < records.length; i += BATCH) {
const slice = records.slice(i, i + BATCH);
const inputs = slice.map(r => `${r.title}\n\n${r.content ?? ''}`);
const { data } = await openai.embeddings.create({
model: 'text-embedding-3-small',
input: inputs,
});
slice.forEach((rec, j) => {
embedded.push({
id: rec.objectID,
title: rec.title,
url: rec.url,
embedding: data[j].embedding, // 1536-dim float array
});
});
console.log(`Embedded ${Math.min(i + BATCH, records.length)} / ${records.length}`);
}
writeFileSync('embedded-records.json', JSON.stringify(embedded));
Step 3 — Build the altor-vec index
Convert your embedded records into a binary index file that altor-vec can load in the browser.
// build-index.mjs
import altorVec from 'altor-vec/node';
import { readFileSync, writeFileSync } from 'fs';
const records = JSON.parse(readFileSync('embedded-records.json', 'utf8'));
const builder = new altorVec.IndexBuilder({ dimensions: 1536 });
for (const rec of records) {
builder.add({
id: rec.id,
vector: new Float32Array(rec.embedding),
metadata: { title: rec.title, url: rec.url },
});
}
const indexBuffer = await builder.build();
writeFileSync('public/search.altorindex', indexBuffer);
console.log(`Index written: ${(indexBuffer.byteLength / 1024).toFixed(1)} KB`);
Step 4 — Wire up browser search
Drop the previous Algolia InstantSearch widget and replace with altor-vec's browser API:
// search.js (browser bundle)
import { AltorIndex } from 'altor-vec';
const idx = await AltorIndex.load('/search.altorindex');
document.getElementById('search-input').addEventListener('input', async (e) => {
const query = e.target.value.trim();
if (!query) return;
const results = await idx.search(query, { topK: 8 });
document.getElementById('results').innerHTML = results.map(r => `
<a href="${r.metadata.url}">
<strong>${r.metadata.title}</strong>
<span>Score: ${r.score.toFixed(3)}</span>
</a>
`).join('');
});
Comparison at a glance
| Dimension | Algolia | altor-vec |
|---|---|---|
| Cost at scale | ~$1 / 1,000 search ops (paid plans) | $0 per query (browser-side) |
| Server required | Yes — Algolia's hosted API | No — runs in WASM in the browser |
| Typo tolerance | Built-in, configurable | None natively (embedding-fuzzy only) |
| Search type | Keyword + neural hybrid | Dense vector (semantic) |
| Real-time updates | Seconds via API push | Rebuild + redeploy index file |
embed-records.mjs + build-index.mjs whenever your content changes, then uploads the resulting search.altorindex to your CDN origin.
Frequently asked questions
How much does migrating from Algolia to altor-vec save?
Algolia's pricing starts at roughly $1 per 1,000 search operations on paid plans. altor-vec runs entirely in the browser with no API calls at query time, so your per-search cost drops to zero. Your only ongoing cost is the one-time embedding generation when you build or refresh your index, which you can do with any embedding provider or a locally-run model.
Can I replicate Algolia's faceted search in altor-vec?
Not natively. altor-vec performs vector similarity search and does not have built-in facet aggregation. You can work around this by pre-filtering your document set in JavaScript before running a vector query, or by storing facet metadata alongside your embeddings and filtering results post-query. For complex filtering trees, Algolia's faceting remains superior.
Will there be downtime during migration?
Migration is additive, not a cutover. You can run both Algolia and altor-vec in parallel during your transition. Export your records, build the altor-vec index as a static asset, and shadow-test results before switching your UI. Most teams complete a shadow period in a few days with zero user-facing downtime.
What query types does altor-vec support compared to Algolia?
altor-vec performs dense vector similarity search — it finds results that are semantically similar to a query even when no keywords overlap. Algolia supports keyword search with typo tolerance and neural/hybrid search. If your users rely on exact phrase matching or typo correction, keep Algolia for those cases or implement a client-side fuzzy-match fallback alongside altor-vec.