Migrating from Algolia or Elasticsearch
Already running search on Algolia or Elasticsearch? Viglet Turing ES keeps the search depth you rely on — facets, typo tolerance, synonyms, relevance tuning — and adds RAG, cited chat and AI agents, while running entirely on your own infrastructure. Your documents, embeddings and query logs never leave your network, and there is no per-record or per-search billing.
This page maps the concepts you already know to their Turing equivalents, then walks the concrete import path. It's a migration, not a rewrite — most of what you built carries over.
Concept mapping
The building blocks line up almost one-to-one. Where a hosted feature is paid, beta, or vendor-locked, Turing ES ships the equivalent in the box, running on the search engine you already operate (Apache Solr, Elasticsearch, or the embedded Lucene index).
| Concept | Algolia | Elasticsearch | Viglet Turing ES |
|---|---|---|---|
| Where documents live | Index | Index | Semantic Navigation (SN) site |
| A single record | Record (JSON object) | Document (_source) | Document — indexed via REST or a connector |
| Schema & field config | Index settings (searchableAttributes…) | Mappings | Field manifest as code — POST /api/sn/manifest |
| Faceting | attributesForFaceting | Aggregations | Native facet fields, no extra query DSL |
| Typo tolerance | typoTolerance | Fuzzy queries | Typo-tolerant + spell-check in the box |
| Synonyms | Synonyms API | Synonym token filter | Engine synonyms (Solr / Elasticsearch) |
| Semantic / vector search | NeuralSearch (paid add-on) | kNN over dense_vector | Embedded Lucene KNN or your engine's vectors, fused via hybrid RRF |
| Relevance tuning | Custom ranking + tie-breaking | BM25 + function_score | BM25 + opt-in hybrid RRF (keyword + vector) |
| AI answers over your content | Ask AI (hosted beta) | ELSER + your own LLM glue | RAG, cited chat & agents built in |
| Front-end UI | InstantSearch.js / React | Build it (elasticsearch-js) | React SDK + zero-dep vanilla-JS SDK |
| Query API | Hosted search API | _search DSL | POST /api/sn/{site}/search — on your host |
| Hosting & data residency | Fully hosted SaaS | Elastic Cloud or self-managed | Self-hosted — content & embeddings stay in your infra |
High-level mapping as of the current release — verify current vendor capabilities against their own docs, as hosted features change often.
The fast path: turing migrate
The turing CLI automates schema translation and record import in a
single command. Point it at your source index — Turing reads the mapping/settings,
derives the field manifest, provisions the SN site, and imports
every record for you. This is the recommended "step 1 + step 2".
# Elasticsearch — URL + credentials + index only, no vendor SDK
turing migrate elasticsearch \
--source-url https://es.example.com:9200 \
--source-user elastic --source-password <password> \
--index products --site Products \
--se-instance <search-engine-instance-id>
# Algolia — schema inferred from a sample of records + your index settings
turing migrate algolia \
--app-id <APP_ID> --api-key <read-capable-key> \
--index catalog --site Catalog \
--se-instance <search-engine-instance-id> --use-llm
Add --dry-run to preview the derived schema (field name, type, facet, multi-valued)
without provisioning or importing anything — review it, then run for real. For
Elasticsearch the mapping is translated deterministically (text→TEXT,
keyword→STRING, numeric/date/boolean → the matching type; keyword/boolean
fields become facets). Algolia has no strict schema, so the types are inferred from
a record sample and refined by your index settings (attributesForFaceting →
facets, searchableAttributes → text); --use-llm sharpens the draft with a
configured LLM. --se-instance is only required when the target site doesn't exist
yet. Any synonyms on an Algolia index are reported so you can apply them in your
target engine's schema (Solr / Elasticsearch).
A source schema is rarely 1:1 with what you want in Turing, so pass
--overrides-file <path> — a JSON array that renames, retypes, drops, or
defaults fields on the way in (applied to both the schema and every record):
[
{ "field": "cost", "rename": "price", "type": "CURRENCY" },
{ "field": "internal_notes", "drop": true },
{ "field": "source", "type": "STRING", "defaultValue": "algolia-import" }
]
Verify relevance parity before cutover. Run your top queries against both the old engine and Turing and diff the results, so you switch on evidence, not hope:
turing migrate compare --engine elasticsearch \
--source-url https://es.example.com:9200 --source-user elastic --source-password "$ES_PASSWORD" \
--index products --site Products --queries-file ./queries.txt --rows 10
The report shows, per query and in aggregate, the result-set overlap (Jaccard), how much of the source's top-N Turing reproduced, and how often the #1 result matches.
Then do only step 3 below — swap the front-end client.
Prefer to drive the import yourself, or scripting it without the CLI? The same result is available over the REST API in three moves.
Doing it by hand: the API path
1. Describe the schema as code
Turn your Algolia index settings or Elasticsearch mappings into a field manifest and POST it once. Turing converges the live SN site to match — creating the site if it's missing and adding fields — and reports exactly what changed. Re-posting the same manifest is a no-op.
curl -X POST "http://localhost:2700/api/sn/manifest" \
-H "Key: <YOUR_API_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"name": "products",
"seInstanceId": "<search-engine-instance-id>",
"schemaVersion": "1",
"locales": ["en_US"],
"fields": [
{ "name": "title", "type": "TEXT", "mandatory": true, "facet": false },
{ "name": "brand", "type": "STRING", "mandatory": false, "facet": true },
{ "name": "price", "type": "CURRENCY", "mandatory": false, "facet": true }
]
}'
Map your field types to the TurSEFieldType enum: INT, LONG, STRING, TEXT, ARRAY, DATE, BOOL, FLOAT, DOUBLE, CURRENCY. Set facet: true for what was attributesForFaceting (Algolia) or a keyword aggregation field (Elasticsearch). Prefer to review the schema in a pull request first? POST /api/sn/manifest/plan returns the diff without mutating anything. See Field Manifest & Schema-as-Code for the full reference, the planner, and breaking-change migrations.
2. Bring your records over
Export your records (Algolia's Browse / export API) or documents (an Elasticsearch _search scroll / _source dump), reshape each into an SN job item, and bulk-index them through the SN import API. Each attribute map must include an id.
curl -X POST "http://localhost:2700/api/sn/import" \
-H "Key: <YOUR_API_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"snJobItems": [
{
"locale": "en_US",
"turSNJobAction": "CREATE",
"siteNames": ["products"],
"attributes": {
"id": "sku-001",
"title": "Wireless Headphones",
"brand": "Acme",
"price": 199.00
}
},
{ "turSNJobAction": "COMMIT", "siteNames": ["products"] }
]
}'
Finish a batch with a COMMIT job item so the search engine flushes pending changes. If your records reference binary files, use the ZIP variant (POST /api/sn/import/zip) — Turing extracts text with Apache Tika before indexing. See Import & Export for job actions and the full item structure.
If your content still lives in a source system (a CMS, a database, a file share), point a connector at it instead of re-exporting from Algolia/Elasticsearch — the connector crawls the source of truth and keeps the index in sync automatically.
3. Swap the front-end client
Replace InstantSearch or the elasticsearch-js client with a Turing SDK. Both are headless — hooks and components for search, facets, autocomplete, and cited chat — so your UI now runs against your own host.
import { useTuringSearch } from "@viglet/turing-react-sdk";
function ProductSearch() {
const { results, facets, setQuery } = useTuringSearch({ site: "products" });
// facets, pagination, typo tolerance and locale come from the same API
return <ResultList results={results} facets={facets} onQuery={setQuery} />;
}
Prefer no framework? The zero-dependency vanilla-JS SDK exposes the same controllers for plain <script> tags, Adobe Edge Delivery blocks, or any bundler. The underlying query API is a single call:
curl -X POST "http://localhost:2700/api/sn/products/search" \
-H "Content-Type: application/json" \
-d '{ "q": "wireless headphones", "fq": ["brand:Acme"], "rows": 20 }'
Your data stays in your infrastructure
The reason teams switch: a hosted SaaS keeps your index — and increasingly your users' queries — in someone else's cloud. Turing ES runs entirely on your own hardware.
- Self-hosted under Apache 2.0 — no license fee, no vendor lock-in.
- Content, embeddings and query logs stay on-premises — nothing leaves your network.
- Bring your own LLM and search engine — OpenAI, Anthropic, Gemini, Azure or local Ollama; Solr, Elasticsearch or embedded Lucene.
- No per-record or per-search billing — cost scales with your infrastructure, not your traffic.
See Deploy & Operate for installation, and Self-hosted, any LLM for the platform overview.
Related Pages
| Page | Description |
|---|---|
| Field Manifest & Schema-as-Code | Provision and evolve the SN field schema declaratively |
| Import & Export | Site config and content import APIs in full |
| Integration | Connectors that crawl a source system into the index |
| Semantic Navigation | How fields are searched, faceted and ranked |
| RAG & Chat | Add cited AI answers over the content you just imported |
| React SDK · JavaScript SDK | Front-end clients that replace InstantSearch |