What is a Search OS?
A search box answers one query. A DIY engine gives you a retrieval primitive. A Search OS is the layered system around it — query understanding, indexing, synonyms, ranking rules, curation, results and analytics — that a tenant actually controls. Here's how AACSearch implements each layer.
Most teams meet search at one of two extremes. On one side is the search box: a hosted widget you paste into a page, which answers queries well enough until someone asks why "кроссовки" returns nothing or why last season's SKU is still pinned to position one. On the other side is the DIY engine: Typesense, Elasticsearch or Meilisearch stood up by the backend team, which gives you a fast retrieval primitive and then leaves synonyms, merchandising, key management, quotas and analytics as a to-do list nobody owns.
A Search OS is the thing in between that both of those are missing: the operating layer around the engine, where every decision that shapes a result — before the query reaches the index and after the hits come back — is a first-class, editable object with an API and a screen.
The layers a tenant controls
Think of a search request as passing through a stack. Each layer is something an operator can inspect and change without touching the one below it.
Query understanding. What did the visitor mean? A query typed on the wrong keyboard layout, transliterated across scripts, or a Cyrillic letter that looks Latin, should still find the product. In AACSearch this is the zero-hit rescue ladder: an empty result is retried as layout-corrected, transliterated and homoglyph-folded variants in one round trip, and the response reports which rewrite fired in query_rewrite so the UI can say "showing results for …". Suggestions and pre-search hints come from POST /collections/{collectionName}/suggest and /presearch (client.queryIntelligence.suggestQueries, presearchQueries in the TypeScript SDK), and the console groups all of it under Query intelligence.
Indexing. Collections and documents are the base resources (/collections, /collections/{collectionName}/documents, plus import and export). Larger loads go through ingest jobs (/ingest-jobs, /ingest-jobs/upload, retry and cancel) and connectors (/connectors) for CMS feeds, so a catalogue refresh is a tracked job with a status, not a script someone ran once. The console exposes these as Collections, Sources and Documents.
Synonyms and language. Synonym sets (/synonym_sets, with per-item CRUD and bulk import/export), stopword sets (/stopwords) and stemming dictionaries (/stemming/dictionaries) are all tenant resources. AACSearch also generates candidates for you: POST /collections/{collectionName}/synonym_suggestions/generate proposes pairs, and each suggestion is accepted or rejected through its own endpoint, so the Synonym suggestions screen is a review queue rather than a blank form.
Ranking rules. A search rule (/collections/{collectionName}/search_rules, client.searchRules.upsertSearchRule) carries boosts, exclusions and a zero-hits fallback for one collection; boosts compile to the engine's own sort syntax at query time. Presets (/presets) hold reusable parameter bundles, and search profiles (/collections/{collectionName}/search-profiles) compile a named configuration a browser key can be locked to. The console splits this across Search rules, Ranking, Presets and Search profiles.
Curation. Pins and hidden items live in curation sets (/curation_sets and their items), campaigns give a rule a start and end date so a seasonal placement expires on its own, and experiments (/experiments with start, stop, promote and promotion/rollback) let you ship a ranking change to a share of sessions before promoting it. Merchandising rules that survive a catalogue refresh covers how to write these so they keep working after the feed changes.
Results. The request itself is POST /collections/{collectionName}/documents/search (client.documents.searchCollection) or /multi_search for federated search. Semantic, image and voice search are sibling endpoints on the same collection (documents/search/semantic, by_image, by_voice), recommendations come from /collections/{collectionName}/recommendations, and a grounded assistant answers over the same index through /assistants/{assistantId}/chat/completions. Recommendations and the assistant retrieve from the same collections, so there is one index to keep correct, not three.
Analytics. Search, click and conversion events arrive server-side through POST /events or from the storefront through POST /analytics/events/batch. Metrics are queryable, not just charted: /analytics/metrics/queries, zero-results, funnel and revenue-attribution (client.analytics.listAnalyticsZeroResultMetrics and friends). The Quality screen runs golden query sets against a candidate configuration before you ship it.
Why these belong in one system
You could assemble each of those layers from separate tools. The reason to keep them in one system is that they share three things a bolt-on cannot.
One tenancy model. Every layer above is scoped to an organization, on a shared engine, by the same mechanism: resource names are prefixed per tenant on the way in and stripped on the way out, events carry the organization id, quotas are enforced by one per-organization Durable Object, and API keys come in two scopes — full for servers, search for browsers — with a contract-level list of what a search key may reach. If synonyms lived in one product and analytics in another, each would need its own copy of that model, and they would drift. Multi-tenant search architecture goes through the isolation layer in detail.
One contract. openapi/openapi.yaml is the source of truth for the whole tenant API. The TypeScript, Python, Go and PHP SDKs and the MCP server are generated from it, and the embedded admin talks to the API through the TypeScript SDK, so a new resource — say, a new kind of curation item — appears in every SDK, in the MCP server an agent uses, and in the admin, from one change. A search box has no contract; a DIY engine's contract stops at the engine.
One admin. The console is not a separate product with its own data model. Its embedded admin, built on ra-core, mounts every contract resource — collections, documents, aliases, synonym sets, curation sets, stopwords, presets, stemming dictionaries, NL search models, search rules, experiments, conversation models, assistants, recommendation models and analytics rules — as plain CRUD tables backed by the same SDK the developer uses. The merchandiser and the engineer are editing the same object.
What this changes for a team
The practical difference is who can act. With a search box, the merchandiser files a ticket. With a DIY engine, the engineer writes a migration. With a Search OS, the person who noticed the problem opens the screen for that layer — Synonyms, Search rules, Curation — changes the object, and the API, the SDKs and the analytics see the change at once, with a lint pass over the rule before it saves.
That is the category AACSearch is built for. Start with the feature tour, the developer overview, or see how the layering compares to a hosted search box in AACSearch vs Algolia.
Related Posts
Faceted search that scales to millions of SKUs
Why facet counts get slow as a catalogue grows, the difference between an exact and an estimated count, and how to keep faceted navigation fast without hiding filters shoppers need.
Webhook delivery: signatures, retries and safe replays
How AACSearch signs outbound webhooks, what happens when your endpoint is briefly down, and how an Idempotency-Key keeps a retried write from landing twice.
Multi-tenant search architecture: how tenant isolation works in AACSearch
Collection namespacing on the engine proxy, full versus search key scopes, short-lived browser keys, per-organization quotas in a Durable Object and organization-scoped events — what a platform operator gets when embedding search for many customers.