> ## Documentation Index
> Fetch the complete documentation index at: https://www.meilisearch.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Edit documents with functions

> Use Rhai scripting functions to transform documents directly inside Meilisearch without re-uploading them.

Meilisearch allows you to edit documents in place using [Rhai](https://rhai.rs/book/) scripting functions. Instead of fetching documents, modifying them externally, and re-indexing, you write a short function that Meilisearch applies to each matching document.

<Note>
  This feature is experimental. Enable it before use and expect its API to change between releases.
</Note>

## When to use functions

* **Bulk field updates**: add, rename, or remove fields across thousands of documents
* **Data normalization**: convert strings to uppercase, trim whitespace, reformat dates
* **Computed fields**: derive new fields from existing ones (e.g. concatenate `firstName` and `lastName` into `fullName`)
* **Conditional edits**: update only documents matching a filter expression

## Enable the feature

Send a `PATCH` request to `/experimental-features`:

<CodeGroup>
  ```bash theme={null}
  curl \
    -X PATCH 'MEILISEARCH_URL/experimental-features' \
    -H 'Content-Type: application/json' \
    -H 'Authorization: Bearer MEILISEARCH_KEY' \
    --data-binary '{
      "editDocumentsByFunction": true
    }'
  ```
</CodeGroup>

## Basic usage

Send a `POST` request to `/indexes/{index_uid}/documents/edit` with a `function` parameter containing Rhai code. The function receives each document as `doc` and can modify its fields directly:

<CodeGroup>
  ```bash theme={null}
  curl \
    -X POST 'MEILISEARCH_URL/indexes/movies/documents/edit' \
    -H 'Content-Type: application/json' \
    -H 'Authorization: Bearer MEILISEARCH_KEY' \
    --data-binary '{
      "function": "doc.title = doc.title.to_upper()"
    }'
  ```
</CodeGroup>

This converts the `title` field to uppercase for every document in the `movies` index. The operation is asynchronous and returns a task object.

## Filter target documents

Use the `filter` parameter to apply the function only to documents matching a filter expression:

<CodeGroup>
  ```bash theme={null}
  curl \
    -X POST 'MEILISEARCH_URL/indexes/movies/documents/edit' \
    -H 'Content-Type: application/json' \
    -H 'Authorization: Bearer MEILISEARCH_KEY' \
    --data-binary '{
      "function": "doc.status = \"archived\"",
      "filter": "release_date < \"2000-01-01\""
    }'
  ```
</CodeGroup>

This sets `status` to `"archived"` only for movies released before the year 2000. The `filter` parameter uses the same [filter expression syntax](/capabilities/filtering_sorting_faceting/advanced/filter_expression_syntax) as search filters. Filtered attributes must be declared in `filterableAttributes`.

## Pass data with context

The `context` parameter lets you pass external data into your function. Access it through the `context` variable:

<CodeGroup>
  ```bash theme={null}
  curl \
    -X POST 'MEILISEARCH_URL/indexes/products/documents/edit' \
    -H 'Content-Type: application/json' \
    -H 'Authorization: Bearer MEILISEARCH_KEY' \
    --data-binary '{
      "function": "if context.discounted_ids.contains(doc.id) { doc.price = doc.price * 0.8 }",
      "context": {
        "discounted_ids": [1, 42, 99, 120]
      },
      "filter": "category = \"electronics\""
    }'
  ```
</CodeGroup>

This applies a 20% discount to specific products in the electronics category.

## Examples

### Add a new field

<CodeGroup>
  ```bash theme={null}
  curl \
    -X POST 'MEILISEARCH_URL/indexes/movies/documents/edit' \
    -H 'Content-Type: application/json' \
    -H 'Authorization: Bearer MEILISEARCH_KEY' \
    --data-binary '{
      "function": "doc.title_upper = doc.title.to_upper()"
    }'
  ```
</CodeGroup>

### Remove a field

<CodeGroup>
  ```bash theme={null}
  curl \
    -X POST 'MEILISEARCH_URL/indexes/users/documents/edit' \
    -H 'Content-Type: application/json' \
    -H 'Authorization: Bearer MEILISEARCH_KEY' \
    --data-binary '{
      "function": "doc.remove(\"temporary_field\")"
    }'
  ```
</CodeGroup>

### Concatenate fields

<CodeGroup>
  ```bash theme={null}
  curl \
    -X POST 'MEILISEARCH_URL/indexes/contacts/documents/edit' \
    -H 'Content-Type: application/json' \
    -H 'Authorization: Bearer MEILISEARCH_KEY' \
    --data-binary '{
      "function": "doc.full_name = `${doc.first_name} ${doc.last_name}`"
    }'
  ```
</CodeGroup>

### Conditional logic

<CodeGroup>
  ```bash theme={null}
  curl \
    -X POST 'MEILISEARCH_URL/indexes/products/documents/edit' \
    -H 'Content-Type: application/json' \
    -H 'Authorization: Bearer MEILISEARCH_KEY' \
    --data-binary '{
      "function": "if doc.stock == 0 { doc.availability = \"out_of_stock\" } else { doc.availability = \"in_stock\" }"
    }'
  ```
</CodeGroup>

### Use context for batch tagging

<CodeGroup>
  ```bash theme={null}
  curl \
    -X POST 'MEILISEARCH_URL/indexes/articles/documents/edit' \
    -H 'Content-Type: application/json' \
    -H 'Authorization: Bearer MEILISEARCH_KEY' \
    --data-binary '{
      "function": "doc.tags = context.tags",
      "context": {
        "tags": ["featured", "2026"]
      },
      "filter": "category = \"blog\""
    }'
  ```
</CodeGroup>

## Rhai language basics

Rhai is a lightweight scripting language. Here are the most common operations for document editing:

| Operation               | Syntax                                         |
| ----------------------- | ---------------------------------------------- |
| Set a field             | `doc.field = value`                            |
| String interpolation    | `` doc.field = `Hello ${doc.name}` ``          |
| Uppercase / lowercase   | `doc.field.to_upper()`, `doc.field.to_lower()` |
| Remove a field          | `doc.remove("field")`                          |
| Conditionals            | `if condition { ... } else { ... }`            |
| Access context          | `context.key`                                  |
| Check if array contains | `array.contains(value)`                        |
| String concatenation    | `"hello" + " " + "world"`                      |
| Math operations         | `doc.price * 0.9`, `doc.count + 1`             |

For the full language reference, see the [Rhai Book](https://rhai.rs/book/).

## Important considerations

* Edit-by-function is an **asynchronous operation**. It returns a task that you can [monitor](/capabilities/indexing/tasks_and_batches/monitor_tasks) like any other indexing task.
* The function runs on **every document** matching the filter (or all documents if no filter is provided). Test on a small subset first using a restrictive filter.
* Edit-by-function tasks **cannot be autobatched** with other task types. Each edit operation runs as its own batch.
* If the function contains a syntax error or runtime error, the task will fail. Check the task's `error` field for details.
* Editing documents triggers a **reindex** of the modified documents.

## Next steps

<CardGroup cols={2}>
  <Card title="Filter expression syntax" href="/capabilities/filtering_sorting_faceting/advanced/filter_expression_syntax">
    Learn the full filter syntax for targeting documents.
  </Card>

  <Card title="Rhai language reference" href="https://rhai.rs/book/">
    Explore the complete Rhai scripting language documentation.
  </Card>

  <Card title="Monitor tasks" href="/capabilities/indexing/tasks_and_batches/monitor_tasks">
    Track the progress of your edit-by-function operations.
  </Card>

  <Card title="Add and update documents" href="/capabilities/indexing/how_to/add_and_update_documents">
    Other ways to modify documents in Meilisearch.
  </Card>
</CardGroup>
