{
  "generator": {
    "name": "Symvanta",
    "url": "https://symvanta.com"
  },
  "page": {
    "url": "https://symvanta.com/architecture/meilisearch",
    "title": "Meilisearch Architecture: How It Actually Works",
    "description": "Meilisearch's 61 clusters centered on the IndexScheduler task queue and the bucket_sort ranking pipeline, mapped by Symvanta.",
    "datePublished": "2026-06-18",
    "dateModified": "2026-08-22"
  },
  "repository": {
    "name": "meilisearch/meilisearch",
    "url": "https://github.com/meilisearch/meilisearch",
    "commit": "577f7af",
    "license": "MIT"
  },
  "graph": {
    "modules": 61,
    "dependencyCycles": 0,
    "modularityQ": 0.78,
    "largestCycleFiles": null,
    "mutuallyRecursiveGroups": 7
  },
  "summaryMarkdown": "Meilisearch is an open-source, Rust-based search engine: a self-hosted alternative to hosted search APIs, built around a fast full-text and vector-ranking core called `milli`. Symvanta's Louvain community detection organized the codebase into 61 functional modules (modularity Q=0.78, generated by [Symvanta](https://symvanta.com)'s code graph). The largest is the `meilisearch` server crate itself at 1728 symbols: the actix route handlers, the request and response types they deserialize, and the `IndexScheduler` handle every handler holds. The product-shaped structures sit inside these clusters as members. `IndexScheduler` owns the durable, async task queue that every write (document ingestion, settings change, index swap) is enqueued onto and processed from; a `Search`/`SearchContext` pair builds and walks a query graph over the index; `bucket_sort` ranks candidates through an ordered chain of ranking rules; and `Embedder` wraps one of several pluggable providers (HuggingFace, OpenAI, Ollama, a generic REST backend, user-supplied vectors, or a composite of an index-time and a search-time embedder) for semantic search. The clustering at this commit labels most modules with the generic Rust types that everything routes through (`Result`, `FieldId`, `Error`, `IndexUid`), so every module name on this page was re-derived from the crates and symbols the cluster actually holds, checked one by one against the graph. The repository also vendors a full typed client for the OpenAI API (`external-crates/async-openai`) and carries a large integration-test harness (`crates/meilisearch/tests/common`); both are real graph clusters but neither is Meilisearch's own architecture. Two subsystems are traced end to end on their own pages: the [indexing pipeline](/architecture/meilisearch/indexing-pipeline), how a document becomes searchable, and the [ranking rules](/architecture/meilisearch/ranking-rules), how results get ordered.",
  "subsystems": [
    {
      "name": "Meilisearch HTTP API",
      "descriptionMarkdown": "Hub `IndexUid`, 1728 symbols, the largest cluster on the map. The `meilisearch` server crate: the actix route handlers, the request and response types they deserialize (`Param`, `SearchQuery`, `IndexUid`), `ResponseError`, and the `AuthController` that gates every call. `IndexScheduler` is declared here too, as the shared state the handlers hold. It makes 245 calls into Scheduler Runtime and Query Execution and 244 into Task and Error Types, because nearly every handler ends by enqueuing a task or reading one back."
    },
    {
      "name": "Scheduler Runtime and Query Execution",
      "descriptionMarkdown": "Hub `Result`, 1413 symbols. Louvain grouped the scheduler loop and milli's query execution into one cluster, and its hub is the crate-local `Result` alias both sides return. It holds `tick`, `register`, `create_next_batch`, `process_batch`, `Queue`, and `IndexMapper` from the index-scheduler crate, next to milli's `Search`, `SearchContext`, `QueryGraph`, `execute_search`, `bucket_sort`, and every `RankingRule` implementation. It calls into Index Storage and Write Pipeline 253 times and into Query Term Interning and Bitmaps 183 times, and takes 447 calls back from the write pipeline. The [indexing pipeline trace](/architecture/meilisearch/indexing-pipeline) follows the scheduler half of this cluster from an HTTP write to a committed index; the [ranking rules trace](/architecture/meilisearch/ranking-rules) follows the query half."
    },
    {
      "name": "Task and Error Types",
      "descriptionMarkdown": "Hub `Result`, 1392 symbols. The `meilisearch-types` task model: `Task`, `KindWithContent`, `Kind`, `Status`, `TaskId`, `Network`, and the error types the other crates return. This is the shape of a single enqueued job (document addition, settings update, index swap, snapshot) moving from `enqueued` through `processing` to `succeeded` or `failed`. It makes 191 calls into the HTTP API cluster and 129 into the settings model, since a settings change is itself dispatched as an asynchronous task."
    },
    {
      "name": "Index Storage and Write Pipeline",
      "descriptionMarkdown": "Hub `Index`, 1369 symbols. milli's LMDB `Index` and the write path that fills it: the `index` entry point, `extract_all`, `write_to_db`, the `ExtractorBbqueueSender` channel between them, and `FilterableAttributesRule`. Its 447 calls into Scheduler Runtime and Query Execution are the heaviest edge on the whole map, and its 301 calls into On-disk Codecs and Field Ids are the second heaviest, because extraction is a loop that resolves document fields to field ids and writes their postings."
    },
    {
      "name": "On-disk Codecs and Field Ids",
      "descriptionMarkdown": "Hub `FieldId` (a `u16` field identifier), 837 symbols. milli's heed codecs and key types: `DocumentId`, `DelAdd`, `FacetGroupKey`, `FacetGroupKeyCodec`, `BytesRefCodec`, `OrderedF64Codec`, and the LMDB key-size bound. It calls into Index Storage and Write Pipeline 230 times and back into Scheduler Runtime and Query Execution 198 times: reads and writes both flow through field identifiers and the on-disk databases they key."
    },
    {
      "name": "Embedders and Vector Search",
      "descriptionMarkdown": "Hub `EmbedError`, 706 symbols. milli's `vector` module: `Embedder` with one variant per configured provider (HuggingFace, OpenAI, Ollama, a generic REST backend, user-provided vectors, and a composite of an index-time and a search-time embedder), plus `RuntimeEmbedder`, `RuntimeFragment`, `Embedding`, `DistributionShift`, and the `EmbedError` family. It calls into Index Storage and Write Pipeline 58 times and into the settings model 40 times, because an embedder is configured per index and rebuilt when those settings change."
    },
    {
      "name": "Vendored code and configuration",
      "descriptionMarkdown": "Four more clusters round out the top ten. Dump Import and Export (hub `Error`, 977 symbols) is the `dump` crate: `IndexMetadata`, `Version`, `UpdateState`, and the per-version readers that import and export a Meilisearch dump. Vendored async-openai Client (hub `OpenAIError`, 735 symbols) is the bundled `external-crates/async-openai` typed client. CLI Options and Runtime Features (hub `default_settings`, 707 symbols) holds `Opt`, `RuntimeTogglableFeatures`, and the API-key bootstrap. Index Settings Model (hub `Setting`, 624 symbols) holds `Settings` and the `Setting<T>` three-state wrapper that lets a configurable field be explicitly set, reset, or left alone rather than defaulting silently."
    }
  ],
  "startReading": {
    "symbols": [
      {
        "name": "IndexScheduler",
        "sourceUrl": "https://github.com/meilisearch/meilisearch/blob/577f7af28942b71782eab1e59f44ad8296ce0a92/crates/index-scheduler/src/lib.rs#L176"
      },
      {
        "name": "Task",
        "sourceUrl": "https://github.com/meilisearch/meilisearch/blob/577f7af28942b71782eab1e59f44ad8296ce0a92/crates/meilisearch-types/src/tasks/mod.rs#L32"
      },
      {
        "name": "Index",
        "sourceUrl": "https://github.com/meilisearch/meilisearch/blob/577f7af28942b71782eab1e59f44ad8296ce0a92/crates/milli/src/index.rs#L129"
      },
      {
        "name": "Search",
        "sourceUrl": "https://github.com/meilisearch/meilisearch/blob/577f7af28942b71782eab1e59f44ad8296ce0a92/crates/milli/src/search/mod.rs#L132"
      },
      {
        "name": "SearchContext",
        "sourceUrl": "https://github.com/meilisearch/meilisearch/blob/577f7af28942b71782eab1e59f44ad8296ce0a92/crates/milli/src/search/new/mod.rs#L77"
      },
      {
        "name": "Interned",
        "sourceUrl": "https://github.com/meilisearch/meilisearch/blob/577f7af28942b71782eab1e59f44ad8296ce0a92/crates/milli/src/search/new/interner.rs#L10"
      },
      {
        "name": "Embedder",
        "sourceUrl": "https://github.com/meilisearch/meilisearch/blob/577f7af28942b71782eab1e59f44ad8296ce0a92/crates/milli/src/vector/embedder/mod.rs#L23"
      },
      {
        "name": "Settings",
        "sourceUrl": "https://github.com/meilisearch/meilisearch/blob/577f7af28942b71782eab1e59f44ad8296ce0a92/crates/meilisearch-types/src/settings.rs#L214"
      },
      {
        "name": "Opt",
        "sourceUrl": "https://github.com/meilisearch/meilisearch/blob/577f7af28942b71782eab1e59f44ad8296ce0a92/crates/meilisearch/src/option.rs#L214"
      },
      {
        "name": "perform_federated_search",
        "sourceUrl": "https://github.com/meilisearch/meilisearch/blob/577f7af28942b71782eab1e59f44ad8296ce0a92/crates/meilisearch/src/search/federated/perform.rs#L57"
      },
      {
        "name": "insert_object",
        "sourceUrl": "https://github.com/meilisearch/meilisearch/blob/577f7af28942b71782eab1e59f44ad8296ce0a92/crates/flatten-serde-json/src/lib.rs#L15"
      }
    ],
    "endpoints": [
      {
        "method": "POST",
        "path": "/indexes/{index_uid}/search"
      },
      {
        "method": "POST",
        "path": "/indexes/{index_uid}/documents"
      },
      {
        "method": "PATCH",
        "path": "/indexes/{index_uid}/settings"
      },
      {
        "method": "POST",
        "path": "/multi-search"
      },
      {
        "method": "GET",
        "path": "/tasks"
      }
    ]
  },
  "requestFlow": [
    {
      "position": 1,
      "symbol": "search_with_post",
      "sourceUrl": "https://github.com/meilisearch/meilisearch/blob/577f7af28942b71782eab1e59f44ad8296ce0a92/crates/meilisearch/src/routes/indexes/search.rs#L869",
      "markdown": "[`search_with_post`](https://github.com/meilisearch/meilisearch/blob/577f7af28942b71782eab1e59f44ad8296ce0a92/crates/meilisearch/src/routes/indexes/search.rs#L869) (and its `GET` counterpart `search_with_url_query`) is the actix handler. It takes a search permit from the `SearchQueue`, builds a `DocumentSearch` around the parsed query, and awaits its `execute`. When the `legacy_search` experimental feature is on it falls back to `legacy_search_with_post` instead, the older path that calls `perform_search` directly."
    },
    {
      "position": 2,
      "symbol": "DocumentSearch",
      "sourceUrl": "https://github.com/meilisearch/meilisearch/blob/577f7af28942b71782eab1e59f44ad8296ce0a92/crates/meilisearch/src/documents_retrieval/mod.rs#L32",
      "markdown": "[`DocumentSearch`](https://github.com/meilisearch/meilisearch/blob/577f7af28942b71782eab1e59f44ad8296ce0a92/crates/meilisearch/src/documents_retrieval/mod.rs#L32) is the request bundle the handler hands off. Its `execute` method (`documents_retrieval/mod.rs:42`) checks that the API key authorizes each index, applies any tenant-token search rules to the filter, preprocesses the filters, and then runs each query through the federated search path. A single-index search runs as a one-query federation, so there is one code path for both."
    },
    {
      "position": 3,
      "symbol": "perform_federated_search",
      "sourceUrl": "https://github.com/meilisearch/meilisearch/blob/577f7af28942b71782eab1e59f44ad8296ce0a92/crates/meilisearch/src/search/federated/perform.rs#L57",
      "markdown": "[`perform_federated_search`](https://github.com/meilisearch/meilisearch/blob/577f7af28942b71782eab1e59f44ad8296ce0a92/crates/meilisearch/src/search/federated/perform.rs#L57) partitions the queries into local indexes and remote network shards, runs the local ones through `SearchByIndex::execute` (`federated/perform.rs:1335`), and merges the results by weighted score. `SearchByIndex::execute` opens the index read transaction and resolves the `SearchKind` for the query: keyword only, semantic only, or hybrid."
    },
    {
      "position": 4,
      "symbol": "prepare_search",
      "sourceUrl": "https://github.com/meilisearch/meilisearch/blob/577f7af28942b71782eab1e59f44ad8296ce0a92/crates/meilisearch/src/search/mod.rs#L1639",
      "markdown": "[`prepare_search`](https://github.com/meilisearch/meilisearch/blob/577f7af28942b71782eab1e59f44ad8296ce0a92/crates/meilisearch/src/search/mod.rs#L1639) builds the `milli::Search` from that query. It applies the deadline, the ranking-score threshold, the filter, the sort criteria, the distinct field, and any dynamic search rules configured on the index."
    },
    {
      "position": 5,
      "symbol": "search_from_kind",
      "sourceUrl": "https://github.com/meilisearch/meilisearch/blob/577f7af28942b71782eab1e59f44ad8296ce0a92/crates/meilisearch/src/search/mod.rs#L2142",
      "markdown": "[`search_from_kind`](https://github.com/meilisearch/meilisearch/blob/577f7af28942b71782eab1e59f44ad8296ce0a92/crates/meilisearch/src/search/mod.rs#L2142) runs it. Keyword and semantic searches both go through `Search::execute`; a hybrid search calls `execute_hybrid` with its semantic ratio, which runs both orderings and blends them."
    },
    {
      "position": 6,
      "symbol": "execute_search",
      "sourceUrl": "https://github.com/meilisearch/meilisearch/blob/577f7af28942b71782eab1e59f44ad8296ce0a92/crates/milli/src/search/new/mod.rs#L812",
      "markdown": "[`execute_search`](https://github.com/meilisearch/meilisearch/blob/577f7af28942b71782eab1e59f44ad8296ce0a92/crates/milli/src/search/new/mod.rs#L812) is what `Search::execute` calls. It resolves the ranking-rule chain for the query, via `get_ranking_rules_for_query_graph_search` for a text query or `get_ranking_rules_for_placeholder_search` for a filter-only browse, checks sort criteria and geo parameters, then calls `bucket_sort`."
    },
    {
      "position": 7,
      "symbol": "bucket_sort",
      "sourceUrl": "https://github.com/meilisearch/meilisearch/blob/577f7af28942b71782eab1e59f44ad8296ce0a92/crates/milli/src/search/new/bucket_sort.rs#L23",
      "markdown": "[`bucket_sort`](https://github.com/meilisearch/meilisearch/blob/577f7af28942b71782eab1e59f44ad8296ce0a92/crates/milli/src/search/new/bucket_sort.rs#L23) walks the ordered chain of `BoxRankingRule` implementations (words, typo tolerance, proximity, attribute rank, sort, word position, and exactness in the default chain) over the candidate document set, applies `apply_distinct_rule` to drop duplicates by the configured distinct field, and returns a `BucketSortOutput` carrying `docids`, `scores`, `all_candidates`, and a `degraded` flag for searches that hit the time budget. `execute_search` copies those into the `documents_ids` and `document_scores` of the `PartialSearchResult` it returns."
    },
    {
      "position": 8,
      "symbol": "compute_facet_distribution_stats",
      "sourceUrl": "https://github.com/meilisearch/meilisearch/blob/577f7af28942b71782eab1e59f44ad8296ce0a92/crates/meilisearch/src/search/mod.rs#L2057",
      "markdown": "[`compute_facet_distribution_stats`](https://github.com/meilisearch/meilisearch/blob/577f7af28942b71782eab1e59f44ad8296ce0a92/crates/meilisearch/src/search/mod.rs#L2057), back in the federated path, builds `ComputedFacets`, and the ranked documents are formatted into `SearchHit`s for the JSON response. The [ranking rules trace](/architecture/meilisearch/ranking-rules) walks steps 6 and 7 in detail."
    }
  ],
  "deepDives": [
    {
      "title": "Meilisearch Indexing Pipeline, Write to Index",
      "url": "https://symvanta.com/architecture/meilisearch/indexing-pipeline",
      "description": "How a document becomes searchable: the IndexScheduler queue, autobatching, the milli extractors, and roaring-bitmap postings."
    },
    {
      "title": "Meilisearch Ranking Rules, Query to Results",
      "url": "https://symvanta.com/architecture/meilisearch/ranking-rules",
      "description": "How Meilisearch orders results: the query graph and the ranking-rule chain (words, typo, proximity, attribute, exactness)."
    }
  ],
  "diagrams": [
    {
      "url": "https://symvanta.com/architecture/meilisearch/module-map.svg",
      "encodingFormat": "image/svg+xml"
    }
  ]
}
