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

# Meilisearch sink

> Stream Postgres changes directly to Meilisearch with Sequin's Meilisearch sink.

The Meilisearch sink indexes documents into a Meilisearch index, using the Meilisearch JSON API.

<Tip>
  This is the reference for the Meilisearch sink. See the [quickstart](/quickstart/meilisearch) for a step-by-step walkthrough or the [how-to guide](/how-to/stream-postgres-to-meilisearch) for an explanation of how to use the Meilisearch sink.
</Tip>

## Configuration

* **Endpoint URL**

  The URL of your Meilisearch server (e.g., `http://localhost:7700` or `https://your-meilisearch-instance.com`).

* **Index name**

  The name of the Meilisearch index where documents will be indexed. The index must exist before imports can work.

* **Primary key**

  The primary key field for the Meilisearch index. (By default, this is `id`)

* **API key**

  The API key for authenticating with your Meilisearch server.

## Transform requirements

Your [transform](/reference/transforms) must return a document matching the schema of the [Meilisearch index](https://www.meilisearch.com/docs/learn/core_concepts/documents).

For example, the following transform ensures the primary key (`id`) is at the top level of the returned map:

```elixir theme={null}
def transform(action, record, changes, metadata) do
  Map.put(record, "id", Kernel.to_string(record["product"]["id"]))
end
```

## Import action

Sequin uses Meilisearch’s [documents API](https://www.meilisearch.com/docs/reference/api/documents#add-or-update-documents) to create or update documents:

* Meilisearch will create a new document or update an existing one based on the `id`.

## API endpoints used by Sequin

Sequin uses the following endpoints of the [Meilisearch Documents API](https://www.meilisearch.com/docs/reference/api/documents):

* `POST /indexes/{index_uid}/documents`\
  For indexing batches of documents or single documents. Meilisearch accepts both.

* `POST /indexes/{index_uid}/documents/delete-batch`\
  For batch deleting documents.

* `GET /tasks/{task_uid}`\
  For checking the status of asynchronous tasks.

Sequin also uses one method of the [Indexes API](https://www.meilisearch.com/docs/reference/api/indexes#get-an-index):

* `GET /indexes/{index_uid}`\
  Only called when you click "Test Connection" in the Sequin console.\
  Successful responses indicate the connection is working and the index exists, but the result is otherwise ignored.

Sequin does not perform any searches.

## Routing

The Meilisearch sink supports dynamic routing of the `action` and `index_name` with [routing functions](/reference/routing).

### Routing Actions

The Meilisearch sink supports three actions:

* `index` - Create or update a document
* `delete` - Delete a document
* `function` - Apply a function-based update using Meilisearch's [documents edit API](https://www.meilisearch.com/docs/reference/api/documents#edit-documents-with-a-function)

### Basic Example

```elixir theme={null}
def route(action, record, changes, metadata) do
  if record["deleted_at"] != nil do
    %{index_name: metadata.table_name, action: "delete"}
  else
    %{index_name: metadata.table_name, action: "index"}
  end
end
```

### Function Updates

The `function` action allows you to update documents using Meilisearch's function expressions. This is useful for:

* Incrementing/decrementing values
* Modifying arrays
* Conditional updates
* Complex transformations that would be difficult with regular indexing

When using `action: "function"`, you must also provide:

* `filter` - A Meilisearch filter expression to select documents to update
* `function` - A function expression using [Rhai (a JavaScript-like language)](https://docs.meilisearch.com/reference/api/documents#edit-documents-with-rhai) to apply to the matched documents
* `context` (optional) - Additional context data for the function

Example routing function with function updates:

```elixir theme={null}
def route(action, record, changes, metadata) do
  # Increment view count when a page is viewed
  if metadata.table_name == "page_views" do
    %{
      action: :function,
      index_name: "pages",
      filter: "id = #{record["page_id"]}",
      function: "doc.view_count += 1"
    }
  # Update inventory count for products
  elsif metadata.table_name == "inventory_updates" do
    %{
      action: :function,
      index_name: "products",
      filter: "sku = '#{record["sku"]}'",
      function: "doc.stock_quantity += context.quantity_change",
      context: %{quantity_change: record["quantity_change"]}
    }
  else
    # Default indexing behavior
    %{index_name: metadata.table_name, action: "index"}
  end
end
```

When not using a routing function, documents will be written to the index specified in the sink configuration.

## Error handling

Common errors that can occur when working with the Meilisearch sink include:

* Connection issues to the Meilisearch server (HTTP vs HTTPS, TCP port, URL typos, etc.)
* Schema mismatches
* Missing primary key fields

When errors occur, they will be visible in the "Messages" tab of the Sequin web console. You can click on a message to see details about the error.\
Error messages from the Meilisearch API are passed through unchanged to the Sequin console, including messages for asynchronous batch imports.
