---
title: "Search Server Indexing"
description: "Search Server Indexing transforms raw MDX documentation and source collections into structured, searchable records and indexes for search backends like local databases, Orama Cloud, Algolia, and Mi..."
last_updated: "2026-10-12T07:53:59.827853+00:00"
canonical_url: "https://www.doc0.dev/docs/e8872a72-1909-479c-b585-dc74c6e26726/technical/core-architecture/search-server-indexing"
---

<details>
<summary>Relevant source files</summary>

The following files were used as context for generating this wiki page:

- [packages/core/src/search/orama/create-server.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/orama/create-server.ts)
- [packages/base-ui/src/components/dialog/search-orama.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/components/dialog/search-orama.tsx)
- [packages/radix-ui/src/components/dialog/search-orama.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/components/dialog/search-orama.tsx)
- [packages/core/src/search/orama-cloud-legacy.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/orama-cloud-legacy.ts)
- [packages/core/src/search/orama-cloud.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/orama-cloud.ts)
- [packages/core/src/search/client/orama-cloud-legacy.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/client/orama-cloud-legacy.ts)
- [packages/core/src/search/client/orama-cloud.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/client/orama-cloud.ts)
- [packages/core/src/search/orama/create-db.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/orama/create-db.ts)
- [packages/core/src/search/flexsearch.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/flexsearch.ts)
- [packages/asyncapi/src/server/index.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/asyncapi/src/server/index.tsx)
- [packages/core/src/search/algolia.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/algolia.ts)
- [packages/create-app/src/plugins/orama-cloud.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/create-app/src/plugins/orama-cloud.ts)
- [packages/preview/src/pages/_api/api/search.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/preview/src/pages/_api/api/search.ts)
- [packages/core/src/search/client/orama-static.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/client/orama-static.ts)
- [packages/create-app/template/+orama-cloud/@app/lib/export-static-indexes.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/create-app/template/%2Borama-cloud/%40app/lib/export-static-indexes.ts)
- [packages/mdx/src/runtime/server.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/runtime/server.ts)
- [packages/core/src/search/server/build-index.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/server/build-index.ts)
- [packages/core/src/search/server/build-doc.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/server/build-doc.ts)
- [packages/core/src/search/client/flexsearch-static.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/client/flexsearch-static.ts)
- [packages/core/src/search/server.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/server.ts)
- [packages/core/src/search/flexsearch/utils.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/flexsearch/utils.ts)
- [packages/core/src/search/mixedbread.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/mixedbread.ts)
- [packages/openapi/src/server/index.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/openapi/src/server/index.tsx)
- [packages/preview/src/pages/...slugs.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/preview/src/pages/%5B...slugs%5D.tsx)
- [packages/core/src/search/orama/search/simple.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/orama/search/simple.ts)
- [packages/core/src/search/orama/search/advanced.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/orama/search/advanced.ts)
- [packages/create-app/template/+orama-cloud/@app/components/search.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/create-app/template/%2Borama-cloud/%40app/components/search.tsx)
- [packages/core/src/search/client/algolia.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/client/algolia.ts)
- [packages/core/src/search/orama/_stemmers.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/orama/_stemmers.ts)
- [packages/core/src/search/client.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/client.ts)
</details>

## Overview

Search Server Indexing transforms raw MDX documentation and source collections into structured, searchable records and indexes for search backends like local databases, Orama Cloud, Algolia, and Mixedbread. It extracts document content, headings, descriptions, and breadcrumbs, supplying consistent search endpoints across localized page trees.

Sources: [packages/core/src/search/server/build-index.ts:6-48](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/server/build-index.ts#L6-L48), [packages/core/src/search/orama/create-server.ts:78-134](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/orama/create-server.ts#L78-L134)

## Document extraction and structured indexing

### SharedIndex and SharedDocument interfaces

The search indexing pipeline transforms page collections into search-ready payloads using two foundational data structures: `SharedIndex` and `SharedDocument`. A `SharedIndex` captures page-level metadata alongside preprocessed `StructuredData`, while `SharedDocument` decomposes that record into granular searchable entries categorized by type (`page`, `heading`, or `text`).

```typescript
export interface SharedIndex {
  id: string;
  title: string;
  description?: string;
  breadcrumbs?: string[];
  tag?: string | string[];
  structuredData: StructuredData;
  url: string;
}

export interface SharedDocument {
  id: string;
  content: string;
  page_id: string;
  type: 'page' | 'heading' | 'text';
  breadcrumbs?: string[];
  tags: string[];
  url: string;
}
```

Sources: [packages/core/src/search/server/build-index.ts:6-22](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/server/build-index.ts#L6-L22), [packages/core/src/search/server/build-doc.ts:3-11](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/server/build-doc.ts#L3-L11)

### Page index building and resolution

The `buildIndexDefault` function resolves structured data from a `Page` object by checking `page.data.structuredData` (evaluating it if defined as a function) or falling back to invoking `page.data.load()` if present. If neither source yields structured data, an error is thrown. Titles default to `page.data.title` or fall back to the filename derived via `basename` and `extname`.

```typescript
export async function buildIndexDefault(page: Page): Promise<SharedIndex> {
  let structuredData: StructuredData | undefined;

  if (page.data.structuredData) {
    structuredData =
      typeof page.data.structuredData === 'function'
        ? await page.data.structuredData()
        : page.data.structuredData;
  } else if ('load' in page.data && typeof page.data.load === 'function') {
    structuredData = (await page.data.load()).structuredData;
  }

  if (!structuredData)
    throw new Error(
      'Cannot find structured data from page, please define the page to index function.',
    );

  return {
    title: page.data.title ?? basename(page.path, extname(page.path)),
    description: page.data.description,
    url: page.url,
    id: page.url,
    structuredData,
  };
}
```
Sources: [packages/core/src/search/server/build-index.ts:24-48](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/server/build-index.ts#L24-L48)

> [!WARNING]
> Omitting structured data preprocessing on a page without a `load` method or static `structuredData` property causes `buildIndexDefault` to throw an explicit runtime error during index compilation.

Sources: [packages/core/src/search/server/build-index.ts:36-39](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/server/build-index.ts#L36-L39)

### Breadcrumb path resolution

The `buildBreadcrumbs` utility constructs hierarchical navigation paths for search records by querying the localized page tree obtained via `source.getPageTree(page.locale)`. It uses `findPath` to locate the target node, pops the final page segment, and collects valid non-empty string names from parent nodes and the page tree root.

```typescript
function isBreadcrumbItem(item: unknown): item is string {
  return typeof item === 'string' && item.length > 0;
}

export function buildBreadcrumbs<C extends LoaderConfig>(
  source: LoaderOutput<C>,
  page: C['page'],
): string[] | undefined {
  const pageTree = source.getPageTree(page.locale);
  const path = findPath(pageTree.children, (node) => node.type === 'page' && node.url === page.url);

  if (path) {
    const breadcrumbs: string[] = [];
    path.pop();

    if (isBreadcrumbItem(pageTree.name)) {
      breadcrumbs.push(pageTree.name);
    }

    for (const segment of path) {
      if (!isBreadcrumbItem(segment.name)) continue;
      breadcrumbs.push(segment.name);
    }

    return breadcrumbs;
  }
}
```
Sources: [packages/core/src/search/server/build-index.ts:50-76](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/server/build-index.ts#L50-L76)

### Document flattening and record generation

The `buildDocuments` function iterates over an array of `SharedIndex` objects to produce a flat list of `SharedDocument` records. Each page generates a primary document record (`type: 'page'`), an optional description record (`type: 'text'`), multiple heading records (`type: 'heading'`), and body text content chunks (`type: 'text'`). 

| Document Type | ID Format | URL Format | Content Source |
| --- | --- | --- | --- |
| `page` | `page.id` | `page.url` | `page.title` |
| `text` (description) | `${page.id}-${id++}` | `page.url` | `page.description` |
| `heading` | `${page.id}-${id++}` | `${page.url}#${heading.id}` | `heading.content` |
| `text` (content chunk) | `${page.id}-${id++}` | `${page.url}#${content.heading}` or `page.url` | `content.content` |

Sources: [packages/core/src/search/server/build-doc.ts:13-68](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/server/build-doc.ts#L13-L68)

## Local Orama database instantiation

### Database Schemas and Document Types

The Orama search engine supports two distinct schema configurations depending on whether simple or advanced search servers are initialized. The simple schema defines fields for `url`, `title`, `breadcrumbs`, `description`, `content`, and `keywords`. The advanced schema incorporates granular fields including `content`, `page_id`, `type`, `breadcrumbs`, an `enum[]` for `tags`, `url`, and a 512-dimensional vector field `embeddings`.

```typescript
export type SimpleDocument = TypedDocument<Orama<typeof simpleSchema>>;
export const simpleSchema = {
  url: 'string',
  title: 'string',
  breadcrumbs: 'string[]',
  description: 'string',
  content: 'string',
  keywords: 'string',
} as const;

export type AdvancedDocument = TypedDocument<Orama<typeof advancedSchema>>;
export const advancedSchema = {
  content: 'string',
  page_id: 'string',
  type: 'string',
  breadcrumbs: 'string[]',
  tags: 'enum[]',
  url: 'string',
  embeddings: 'vector[512]',
} as const;
```
Sources: [packages/core/src/search/orama/create-db.ts:11-30](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/orama/create-db.ts#L11-L30)

> [!NOTE]
> Advanced search vector queries require installing `@orama/plugin-embeddings` separately; invoking vector mode without this plugin throws an explicit runtime configuration error.

Sources: [packages/core/src/search/orama/create-server.ts:121-128](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/orama/create-server.ts#L121-L128)

### Database Creation and Index Insertion

The `createDB` and `createDBSimple` functions instantiate in-memory Orama databases and populate them with records. If `indexes` is supplied as a dynamic function, it is awaited before index processing. Custom tokenizers take precedence when passed in configuration options, falling back to existing component tokenizers.

```typescript
export async function createDB({
  indexes,
  tokenizer,
  search: _,
  ...rest
}: AdvancedOptions): Promise<Orama<typeof advancedSchema>> {
  const items = typeof indexes === 'function' ? await indexes() : indexes;

  const db = create({
    schema: advancedSchema,
    ...rest,
    components: {
      ...rest.components,
      tokenizer: tokenizer ?? rest.components?.tokenizer,
    },
  }) as Orama<typeof advancedSchema>;

  const mapTo: PartialSchemaDeep<AdvancedDocument>[] = buildDocuments(items);
  await insertMultiple(db, mapTo);
  return db;
}
```
Sources: [packages/core/src/search/orama/create-db.ts:32-52](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/orama/create-db.ts#L32-L52)

```typescript
export async function createDBSimple({
  indexes,
  tokenizer,
  ...rest
}: SimpleOptions): Promise<Orama<typeof simpleSchema>> {
  const items = typeof indexes === 'function' ? await indexes() : indexes;
  const db = create({
    schema: simpleSchema,
    ...rest,
    components: {
      ...rest.components,
      tokenizer: tokenizer ?? rest.components?.tokenizer,
    },
  }) as Orama<typeof simpleSchema>;

  await insertMultiple(
    db,
    items.map((page) => ({
      title: page.title,
      description: page.description,
      breadcrumbs: page.breadcrumbs,
      url: page.url,
      content: page.content,
      keywords: page.keywords,
    })),
  );

  return db;
}
```
Sources: [packages/core/src/search/orama/create-db.ts:54-82](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/orama/create-db.ts#L54-L82)

### Stemmers and Internationalized Tokenizer Resolution

Internationalized search servers resolve language stemmers using a predefined mapping of full language names to language codes. The `STEMMERS` dictionary defines 32 supported language mappings for tokenization and stemming.

| Language Name | Code | Language Name | Code | Language Name | Code |
| --- | --- | --- | --- | --- | --- |
| `arabic` | `ar` | `irish` | `ie` | `slovenian` | `ru` |
| `armenian` | `am` | `italian` | `it` | `spanish` | `es` |
| `bulgarian` | `bg` | `lithuanian` | `lt` | `swedish` | `se` |
| `czech` | `cz` | `nepali` | `np` | `tamil` | `ta` |
| `danish` | `dk` | `norwegian` | `no` | `turkish` | `tr` |
| `dutch` | `nl` | `portuguese` | `pt` | `ukrainian` | `uk` |
| `english` | `en` | `romanian` | `ro` | `vietnamese` | `vi` |
| `finnish` | `fi` | `russian` | `ru` | `sanskrit` | `sk` |
| `french` | `fr` | `serbian` | `rs` | | |
| `german` | `de` | | | | |
| `greek` | `gr` | | | | |
| `hungarian` | `hu` | | | | |
| `indian` | `in` | | | | |
| `indonesian` | `id` | | | | |

Sources: [packages/core/src/search/orama/_stemmers.ts:2-33](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/orama/_stemmers.ts#L2-L33)

The `getTokenizer` helper checks if a given locale string matches a registered stemmer key, returning the corresponding language code or falling back directly to the input locale string.

```typescript
function getTokenizer(locale: string): { language: string } | { tokenizer: Tokenizer } {
  return {
    language: Object.keys(STEMMERS).find((lang) => STEMMERS[lang] === locale) ?? locale,
  };
}
```
Sources: [packages/core/src/search/orama/create-server.ts:172-176](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/orama/create-server.ts#L172-L176)

## Flexsearch index generation and routing

### Initialization and Execution Walkthrough

Multi-locale Flexsearch indices are constructed and served by navigating through a strict call chain. The process executes through the following steps: `flexsearchI18n` calls `serverI18n`, which invokes `initSearchServers` to group indexes by locale and initialize per-locale servers via `server`. Each `server` call invokes `initIndex`, which builds documents via `buildDocuments` and generates unique identifiers using `nextId`.

```mermaid
sequenceDiagram
    participant flexsearchI18n as flexsearch.ts
    participant serverI18n as flexsearch.ts
    participant initSearchServers as flexsearch.ts
    participant server as flexsearch.ts
    participant initIndex as flexsearch.ts
    participant buildDocuments as build-doc.ts
    participant nextId as build-doc.ts

    flexsearchI18n->>serverI18n: flexsearchI18n(options)
    serverI18n->>initSearchServers: initSearchServers()
    initSearchServers->>server: server({ indexes: list, document })
    server->>initIndex: initIndex(indexes)
    initIndex->>buildDocuments: buildDocuments(indexes)
    buildDocuments->>nextId: nextId()
```

Sources: [packages/core/src/search/flexsearch.ts:41-137](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/flexsearch.ts#L41-L137), [packages/core/src/search/server/build-doc.ts:13-68](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/server/build-doc.ts#L13-L68)

> [!NOTE]
> When `flexsearchFromSource` evaluates a loader function, it caches the resulting promise in a `WeakMap` mapped to the specific loader output instance. Re-indexing occurs automatically once a distinct loader instance is returned.

Sources: [packages/core/src/search/flexsearch.ts:149-186](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/flexsearch.ts#L149-L186)

### Document Construction and Search Options

The Flexsearch document schema configures tokenization, indexed fields, and storage options. By default, `createDocument` sets `tokenize` to `'full'`, indexes the `content` field, tags records using `tags`, and enables `store`.

```typescript
export function createDocument(options?: DocumentOptions<Doc>) {
  return new Search.Document<Doc>({
    tokenize: 'full',
    ...options,
    document: {
      id: 'id',
      index: ['content'],
      tag: ['tags'],
      store: true,
      ...options?.document,
    },
  });
```
Sources: [packages/core/src/search/flexsearch/utils.ts:70-81](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/flexsearch/utils.ts#L70-L81)

| Parameter / Property | Type | Default Value | Purpose |
| --- | --- | --- | --- |
| `tokenize` | string | `'full'` | Sets the string tokenization strategy for indexing. |
| `document.id` | string | `'id'` | Specifies the unique document identifier field. |
| `document.index` | string[] | `['content']` | Defines the document fields included in the search index. |
| `document.tag` | string[] | `['tags']` | Defines the fields used for tag-based filtering. |
| `document.store` | boolean | `true` | Enables field storage within the index for retrieval. |

Sources: [packages/core/src/search/flexsearch/utils.ts:70-81](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/flexsearch/utils.ts#L70-L81)

> [!WARNING]
> Setting a `localeMap` entry to the literal string `'cjk'` automatically forces the document encoder configuration to `Search.Charset.CJK`. Any custom document options passed alongside are overridden or merged depending on the locale map specification.

Sources: [packages/core/src/search/flexsearch.ts:31-39](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/flexsearch.ts#L31-L39), [packages/core/src/search/flexsearch.ts:96-100](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/flexsearch.ts#L96-L100)

## Algolia search index synchronization

### Synchronization Flow and Settings Configuration

Synchronization is executed by the `sync` function, which calls `setIndexSettings` followed by `updateDocuments`. The `indexName` defaults to `'document'` when omitted in `SyncOptions`.

```typescript
export async function sync(client: Algoliasearch, options: SyncOptions): Promise<void> {
  const { indexName = 'document', documents } = options;
  await setIndexSettings(client, indexName);
  await updateDocuments(client, indexName, documents);
}
```
Sources: [packages/core/src/search/algolia.ts:49-53](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/algolia.ts#L49-L53)

The index settings are configured via `client.setSettings` with specific attributes for retrieval, faceting, searchability, and distinct filtering.

```typescript
export async function setIndexSettings(client: Algoliasearch, indexName: string): Promise<void> {
  await client.setSettings({
    indexName,
    indexSettings: {
      attributeForDistinct: 'page_id',
      attributesToRetrieve: ['title', 'section', 'content', 'url', 'section_id', 'breadcrumbs'],
      searchableAttributes: ['title', 'section', 'content'],
      attributesToSnippet: [],
      attributesForFaceting: ['tag'],
    },
  });
}
```
Sources: [packages/core/src/search/algolia.ts:55-66](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/algolia.ts#L55-L66)

| Setting Field | Target Value / Type | Purpose |
| --- | --- | --- |
| `attributeForDistinct` | `'page_id'` | Groups search results by page identifier using distinct records. |
| `attributesToRetrieve` | `['title', 'section', 'content', 'url', 'section_id', 'breadcrumbs']` | Specifies the record attributes returned in search queries. |
| `searchableAttributes` | `['title', 'section', 'content']` | Defines attributes included in search ranking and matching. |
| `attributesToSnippet` | `[]` | Configures snippet generation for retrieved records. |
| `attributesForFaceting` | `['tag']` | Enables tag-based faceting and filtering on records. |

Sources: [packages/core/src/search/algolia.ts:58-64](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/algolia.ts#L58-L64)

> [!NOTE]
> The `sync` function replaces all objects in the target index rather than performing incremental updates, ensuring stale records are purged during synchronization.

Sources: [packages/core/src/search/algolia.ts:49-53](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/algolia.ts#L49-L53), [packages/core/src/search/algolia.ts:119-122](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/algolia.ts#L119-L122)

### Record Batch Transformation

The `toIndex` function processes each `DocumentRecord` to generate an array of `BaseIndex` objects. If a page contains a description, an initial record is created with `undefined` for `section` and `section_id`.

```typescript
function toIndex(page: DocumentRecord): BaseIndex[] {
  let id = 0;
  const indexes: BaseIndex[] = [];
  const scannedHeadings = new Set<string>();

  function createIndex(
    section: string | undefined,
    sectionId: string | undefined,
    content: string,
  ): BaseIndex {
    return {
      objectID: `${page._id}-${(id++).toString()}`,
      breadcrumbs: page.breadcrumbs,
      title: page.title,
      url: page.url,
      page_id: page._id,
      tag: page.tag,
      section,
      section_id: sectionId,
      content,
      ...page.extra_data,
    };
  }

  if (page.description) indexes.push(createIndex(undefined, undefined, page.description));
```
Sources: [packages/core/src/search/algolia.ts:68-92](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/algolia.ts#L68-L92)

For each content item in `page.structured`, matching headings are resolved. When a heading is encountered for the first time, a dedicated heading index record is created and added before the content record.

```typescript
const { headings, contents } = page.structured;

for (const p of contents) {
  const heading = p.heading ? headings.find((h) => p.heading === h.id) : null;

  const index = createIndex(heading?.content, heading?.id, p.content);

  if (heading && !scannedHeadings.has(heading.id)) {
    scannedHeadings.add(heading.id);

    indexes.push(createIndex(heading.content, heading.id, heading.content));
  }

  indexes.push(index);
}
```
Sources: [packages/core/src/search/algolia.ts:93-107](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/algolia.ts#L93-L107)

```typescript

  return indexes;
}
```
Sources: [packages/core/src/search/algolia.ts:108-110](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/algolia.ts#L108-L110)

> [!WARNING]
> Object identifiers are constructed by appending an incrementing index counter to `page._id` separated by a hyphen (`${page._id}-${(id++).toString()}`). Collisions will occur if multiple documents share the same `_id`.

Sources: [packages/core/src/search/algolia.ts:78-90](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/algolia.ts#L78-L90)

## Orama Cloud integration and export

### Structured Document Export and Index Transformation

The `exportSearchIndexes` function extracts pages from the source collection and maps each entry into an `OramaDocument` containing its unique identifier, URL, title, description, and structured data.

```typescript
export async function exportSearchIndexes() {
  return source.getPages().map((page) => {
    return {
      id: page.url,
      structured: page.data.structuredData,
      url: page.url,
      title: page.data.title,
      description: page.data.description,
    } satisfies OramaDocument;
  });
}
```
Sources: [packages/create-app/template/+orama-cloud/@app/lib/export-static-indexes.ts:4-14](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/create-app/template/%2Borama-cloud/%40app/lib/export-static-indexes.ts#L4-L14)

The `toIndex` function converts each `OramaDocument` into an array of `OramaIndex` records by iterating over structured contents and matching headings. If a page description exists, an initial record is created with undefined section identifiers.

```typescript
function toIndex(page: OramaDocument): OramaIndex[] {
  let id = 0;
  const indexes: OramaIndex[] = [];
  const scannedHeadings = new Set<string>();

  function createIndex(
    section: string | undefined,
    sectionId: string | undefined,
    content: string,
  ): OramaIndex {
    return {
      id: `${page.id}-${(id++).toString()}`,
      title: page.title,
      url: page.url,
      page_id: page.id,
      tag: page.tag,
      section,
      section_id: sectionId,
      content,
      breadcrumbs: page.breadcrumbs,
      ...page.extra_data,
    };
  }

  if (page.description) indexes.push(createIndex(undefined, undefined, page.description));

```
Sources: [packages/core/src/search/orama-cloud.ts:118-143](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/orama-cloud.ts#L118-L143)

```typescript
page.structured.contents.forEach((p) => {
  const heading = p.heading ? page.structured.headings.find((h) => p.heading === h.id) : null;

  const index = createIndex(heading?.content, heading?.id, p.content);

  if (heading && !scannedHeadings.has(heading.id)) {
    scannedHeadings.add(heading.id);

    indexes.push(createIndex(heading.content, heading.id, heading.content));
  }

  indexes.push(index);
});

return indexes;
```
Sources: [packages/core/src/search/orama-cloud.ts:144-158](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/orama-cloud.ts#L144-L158)

> [!WARNING]
> Unique index identifiers are generated by combining the document ID with an incrementing counter separated by a hyphen (``${page.id}-${(id++).toString()}``).

Sources: [packages/core/src/search/orama-cloud.ts:123-140](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/orama-cloud.ts#L123-L140)

### Remote Synchronization and Framework Endpoints

The `sync` function opens a transaction on the specified Orama Cloud index, inserts the flattened document records, and commits the transaction if `autoDeploy` is enabled.

```typescript
export async function sync(orama: OramaCloud, options: SyncOptions): Promise<void> {
  const { autoDeploy = true } = options;
  const index = orama.index.set(options.index);
  await index.transaction.open();

  await index.transaction.insertDocuments(
    options.documents.flatMap(toIndex) as unknown as AnyObject[],
  );

  if (autoDeploy) await index.transaction.commit();
}
```
Sources: [packages/core/src/search/orama-cloud.ts:89-99](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/orama-cloud.ts#L89-L99)

Internationalized synchronization is handled by `syncI18n`, which maps document locales to corresponding index identifiers and executes concurrent synchronization tasks for each locale.

```typescript
export async function syncI18n(orama: OramaCloud, options: I18nSyncOptions): Promise<void> {
  const { autoDeploy = true, indexes } = options;

  const tasks = options.documents.map(async (document) => {
    const index = orama.index.set(indexes[document.locale]);
    await index.transaction.open();

    await index.transaction.insertDocuments(
      document.items.flatMap(toIndex) as unknown as AnyObject[],
    );

    if (autoDeploy) await index.transaction.commit();
  });

  await Promise.all(tasks);
}
```
Sources: [packages/core/src/search/orama-cloud.ts:101-116](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/orama-cloud.ts#L101-L116)

Framework routing plugins configure static JSON export endpoints across different application routers.

| Framework Target | Route Path | Implementation Handler |
| --- | --- | --- |
| Next.js App Router | `/static.json` | `Response.json(await exportSearchIndexes())` |
| React Router / SPA | `static.json` | `loader` returning `Response.json(await exportSearchIndexes())` |
| Tanstack Start | `/static.json` | Server file route with `GET` handler returning `Response.json(await exportSearchIndexes())` |
| Waku | `/static.json` | `GET` handler with static configuration |

Sources: [packages/create-app/src/plugins/orama-cloud.ts:37-65](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/create-app/src/plugins/orama-cloud.ts#L37-L65), [packages/create-app/src/plugins/orama-cloud.ts:109-140](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/create-app/src/plugins/orama-cloud.ts#L109-L140)

## Mixedbread vector and semantic indexing

### Semantic Chunk Transformation and Endpoint Configuration

The `createMixedbreadSearchAPI` factory constructs a search endpoint that queries Mixedbread vector stores using `@mixedbread/sdk` clients. Options allow configuring client instances, store identifiers, top-k limits, and semantic options like re-ranking and query rewriting.

```typescript
export interface MixedbreadSearchOptions {
  /**
   * The Mixedbread SDK client instance
   */
  client: Mixedbread;

  /**
   * The identifier of the store to search in
   */
  storeIdentifier: string;

  /**
   * Maximum number of results to return
   *
   * @defaultValue 10
   */
  topK?: number;

  /**
   * Re-rank search results for improved relevance (this adds latency to the search)
   */
  rerank?: boolean;

  /**
   * Rewrite the query for better search results (this adds latency to the search)
   */
  rewriteQuery?: boolean;

  /**
   * Minimum score threshold for results
   */
  scoreThreshold?: number;

  /**
   * Custom transform function for search results
   */
  transform?: (results: StoreSearchResult[], query: string) => SortedResult[];
}
```
Sources: [packages/core/src/search/mixedbread.ts:20-57](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/mixedbread.ts#L20-L57)

```typescript
export function createMixedbreadSearchAPI(options: MixedbreadSearchOptions): SearchAPI {
  const {
    client,
    storeIdentifier,
    topK = 10,
    rerank,
    rewriteQuery,
    scoreThreshold,
    transform,
  } = options;

```
Sources: [packages/core/src/search/mixedbread.ts:113-123](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/mixedbread.ts#L113-L123)

> [!CAUTION]
> Exporting search indexes is explicitly unsupported for Mixedbread endpoints; attempting to invoke `export()` throws an error instructing administrators to use the Mixedbread dashboard for store management.

Sources: [packages/core/src/search/mixedbread.ts:167-172](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/mixedbread.ts#L167-L172)

### Result Formatting and Filtering

The default transformation engine processes raw store search results by extracting document metadata such as URLs and titles, producing structured page and heading entries. Headings are cleaned using `remove-markdown` and converted into URL anchor links via `github-slugger`.

```typescript
function defaultTransform(results: StoreSearchResult[]): SortedResult[] {
  return results.flatMap((item) => {
    const metadata = item.generated_metadata;

    const url = metadata.url || '#';
    const title = metadata.title || 'Untitled';

    const chunkResults: SortedResult[] = [
      {
        id: `${item.file_id}-${item.chunk_index}-page`,
        type: 'page',
        content: title,
        url,
      },
    ];

    const headingTitle = item.type === 'text' && item.text ? extractHeadingTitle(item.text) : '';

    if (headingTitle) {
      slugger.reset();

      chunkResults.push({
        id: `${item.file_id}-${item.chunk_index}-heading`,
        type: 'heading',
        content: headingTitle,
        url: `${url}#${slugger.slug(headingTitle)}`,
      });
    }

    return chunkResults;
  });
}
```
Sources: [packages/core/src/search/mixedbread.ts:80-111](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/mixedbread.ts#L80-L111)

Search requests support optional tag filters applied directly to generated metadata fields within store queries.

```typescript
const { tag, limit } = searchOptions;
let filters: StoreSearchParams['filters'] | undefined;
if (Array.isArray(tag) && tag.length > 0) {
  filters = {
    key: 'generated_metadata.tag',
    operator: 'in',
    value: tag,
  };
} else if (typeof tag === 'string') {
  filters = {
    key: 'generated_metadata.tag',
    operator: 'eq',
    value: tag,
  };
}
```
Sources: [packages/core/src/search/mixedbread.ts:130-144](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/search/mixedbread.ts#L130-L144)

## Related

- [Search Client Integrations](https://www.doc0.dev/docs/e8872a72-1909-479c-b585-dc74c6e26726/technical/core-architecture/search-client-integrations)


## Sitemap

See the full [sitemap](https://www.doc0.dev/docs/e8872a72-1909-479c-b585-dc74c6e26726/llms.txt) for all pages in this wiki.
