发布日期: 2025-07-07
版本号: v1.16.0-rc.0

Meilisearch v1.16 候选发布版引入了两个核心新功能:多模态嵌入和实例间文档导出/传输。多模态嵌入功能允许在文档中索引图像、文本和其他格式,并支持使用图像或文本查询来检索文档,该功能需要启用实验性特性并配置支持多模态的嵌入服务。实例间文档传输功能允许通过单一API端点将文档从一个实例流式传输到另一个实例,无需创建转储或快照,方便迁移至云端。此外,该版本还包含了多项改进,如支持嵌套通配符、优化地理字段提取和转储导入性能,以及修复了包括混合搜索去重、西里尔字母拼写容忍度、Gemini集成等多个问题。发布还涵盖了依赖项更新和大量的集成测试优化。请注意,此为候选发布版,不建议在生产环境使用,并且已知从包含嵌入向量的数据库升级至RC.0版本无法正常工作。

更新内容 (中文)

[!WARNING] 由于这是发布候选版(RC),我们不建议在生产环境中使用它。遇到功能异常?欢迎提交错误报告新功能反馈。 ⚠️ 特别注意:已知从包含嵌入向量的数据库升级至RC.0版本无法正常工作 ⚠️

Meilisearch v1.16 引入两大核心功能:多模态嵌入向量支持图像、文本等多种格式的索引;实例间文档导出/迁移功能,可轻松将本地 Meilisearch 实例迁移至云端!

新功能与更新 🔥

多模态嵌入向量

Meilisearch 现已支持便捷索引文档中的图像、文本及其他格式数据,并可通过图像或文本查询检索文档。

该功能利用多模态嵌入器,为图像、文本及所选模型支持的所有数据格式提供统一语义表示。

以示例 movies 数据集为例,其中包含电影描述和海报链接。借助此新功能,可使用文本同时搜索描述内容和海报图像,或通过图像检索视觉相似的海报,亦或查找描述与查询图像匹配的电影。

使用方法

启用多模态功能需先开启实验性特性 multimodal

curl $MEILISEARCH_URL/experimental-features -X PATCH -H 'Content-type: application/json' -d '{"multimodal": true}'

随后选择支持多模态的嵌入服务(如 cohereVoyageAI)开始构建嵌入配置。

以下为 VoyageAI 索引电影数据库描述和海报,并支持文本/图像搜索的配置示例:

// PATCH /indexes/$INDEX_NAME/settings/embedders
{"voyage": {
  "source": "rest",
  "url": "https://api.voyageai.com/v1/multimodalembeddings",
  "apiKey": "$VOYAGE_API_KEY",
  // 定义文档索引方式
  "indexingFragments": {
    "text": {
      "value": {
        // 遵循 Voyage API 规范 <https://docs.voyageai.com/reference/multimodal-embeddings-api>
        "content": [
          {
            "type": "text",
            "text": "电影《{{doc.title}}》描述开头为:{{doc.overview|truncateWords:20}}。"
          }
        ]
      }
    },
    // 同时发送海报数据
    "poster": {
      "value": {
        "content": [
          {
            "type": "image_url",
            "image_url": "{{doc.poster}}"
          }
        ]
      }
    }
  },
  // 定义索引搜索方式
  "searchFragments": {
    // 允许搜索时发送海报图像
    "poster": {
      "value": {
        "content": [
          {
            "type": "image_url",
            "image_url": "{{media.poster}}"
          }
        ]
      }
    },
    // 仅允许搜索时内联图像数据
    "image": {
      "value": {
        "content": [
          {
            "type": "image_base64",
            "image_base64": "data:{{media.image.mime}};base64,{{media.image.data}}"
          }
        ]
      }
    },
    "text": {
      "value": {
        "content": [
          {
            "type": "text",
            // 使用搜索查询中的 `q` 字段
            "text": "{{q}}"
          }
        ]
      }
    }
  },
  "request": {
    "inputs": [
      "{{fragment}}",
      "{{..}}"
    ],
    "model": "voyage-multimodal-3"
  },
  "response": {
    "data": [
      {
        "embedding": "{{embedding}}"
      },
      "{{..}}"
    ]
  }
}}

通过海报(图像URL)搜索示例:

// POST /indexes/$INDEX_NAME/search
{
  "media": {
    "poster": "https://image.tmdb.org/t/p/w500/6FfCtAuVAW8XJjZ7eWeLibRLWTw.jpg"
  },
  "hybrid": {
    "embedder": "voyage",
    // 此处显式设置语义比例,
    // 若省略则默认为1.0(因当前无文本查询)
    "semanticRatio": 1.0
  }
}

通过图像数据(base64编码)搜索示例:

// POST /indexes/$INDEX_NAME/search

{
  "media": {
    "image": {
      "mime": "image/jpeg",
      // base64编码的图像数据
      "data": "/9j/4AAQSkZJRgABAQAAAQABAAD/2w..."
    }
  "hybrid": {
    "embedder": "voyage",
  }
}

通过文本搜索(混合搜索)示例:

// POST /indexes/$INDEX_NAME/search
{
  // 注意:因使用多模态模型,查询生成的嵌入向量也会与电影海报图像对比,
  // 因此描述海报图像的查询可能匹配到相关电影
  "q": "太空中的光剑电影",
  "hybrid": {
    "embedder": "voyage",
    "semanticRatio": 0.5
  }
}

功能详情请参阅使用指南

由 @dureuill 在 #5596 中实现

实例间文档导出/迁移

Meilisearch 现在支持在不创建转储或快照的情况下,直接将文档从一个实例迁移至另一个实例。该功能可通过流式传输简化本地实例向云端迁移的流程。

使用方法

实例仅暴露单一路由接口,支持以下参数:

  • url:目标实例地址(接收设置和文档)
  • apiKey:(可选)具有发送权限的API密钥,通常为远程实例的主密钥
  • payloadSize:(可选)人类可读的负载大小单位,默认50 MiB
  • indexes:(可选)匹配待导出索引的模式集,未定义时默认导出所有索引
  • filter:(可选)定义实际导出的文档子集的过滤条件
  • overrideSettings:(可选,默认false)若为false,则不会覆盖远程已存在索引的设置和主键,但仍会发送文档

POST: /export

{
  "url": "http://localhost:7711",
  "apiKey": null,
  "payloadSize": "123 MiB",
  "indexes": {
    "*": {
      "filter": null,
      "overrideSettings": true
    }
  }
}

响应示例:

{
  "taskUid": 2,
  "indexUid": null,
  "status": "enqueued",
  "type": "export",
  "enqueuedAt": "2025-06-26T12:54:10.785864Z"
}

任务支持失败重试(有限次数)和取消操作。但需注意:取消导出任务不会中止目标实例已接收的任务。

由 @kerollmops 在 @mubelotix 协助下于 #5670 实现

其他改进

修复 🐞

杂项

❤️ 再次感谢所有外部贡献者:

  • Meilisearch:@martin-g、@lblack00、@mcmah309、@nnethercott、@arthurgousset、@Mubelotix、@diksipav、@Nymuxyzo
  • Arroy:@nnethercott

更新内容 (原始)

[!WARNING] Since this is a release candidate (RC), we do NOT recommend using it in a production environment. Is something not working as expected? We welcome bug reports and feedback about new features. ⚠ In particular, upgrading from a database containing embeddings is known not to work in RC.0 ⚠

Meilisearch v1.16 introduces two core features, multimodal embeddings allowing to index images, text and other formats; and the Export/Transfer of documents between instances easing the migration of a local Meilisearch into the cloud!

New features and updates 🔥

Multimodal embeddings

Meilisearch now allows conveniently indexing images, text and other formats in documents, and to retrieve documents by searching with an image or a text query.

This new feature leverages multimodal embedders to provide a common semantic representation for images, texts, and any other piece of data in a format supported by the chosen model.

As an example, the sample movies dataset contains movie descriptions and links to movie posters. Using this new feature, it is possible to use text to search both in the descriptions and the posters, or to use an image to search for similar looking posters, or movies with a description matching the query image.

Usage

To use multimodal embeddings, start by enabling the multimodal experimental feature:

curl $MEILISEARCH_URL/experimental-features -X PATCH -H 'Content-type: application/json' -d '{"multimodal": true}'

Then pick an embedder service that supports multimodal such as cohere or VoyageAI to start building the embedding configuration.

An example configuration for VoyageAI to index the description and poster from the movies database, and to search by text or image, might look like the following:

// PATCH /indexes/$INDEX_NAME/settings/embedders
{"voyage": {
  "source": "rest",
  "url": "https://api.voyageai.com/v1/multimodalembeddings",
  "apiKey": "$VOYAGE_API_KEY",
  // describes how we index documents
  "indexingFragments": {
    "text": {
      "value": {
        // this part sticks to the voyage API <https://docs.voyageai.com/reference/multimodal-embeddings-api>
        "content": [
          {
            "type": "text",
            "text": "A movie titled {{doc.title}} whose description starts with {{doc.overview|truncateWords:20}}."
          }
        ]
      }
    },
    // we also send the poster
    "poster": {
      "value": {
        "content": [
          {
            "type": "image_url",
            "image_url": "{{doc.poster}}"
          }
        ]
      }
    }
  },
  // describes how to search in the index
  "searchFragments": {
    // we allow sending a poster at search time
    "poster": {
      "value": {
        "content": [
          {
            "type": "image_url",
            "image_url": "{{media.poster}}"
          }
        ]
      }
    },
    // we also allow inline image data at search time only
    "image": {
      "value": {
        "content": [
          {
            "type": "image_base64",
            "image_base64": "data:{{media.image.mime}};base64,{{media.image.data}}"
          }
        ]
      }
    },
    "text": {
      "value": {
        "content": [
          {
            "type": "text",
            // uses the `q` field from search queries
            "text": "{{q}}"
          }
        ]
      }
    }
  },
  "request": {
    "inputs": [
      "{{fragment}}",
      "{{..}}"
    ],
    "model": "voyage-multimodal-3"
  },
  "response": {
    "data": [
      {
        "embedding": "{{embedding}}"
      },
      "{{..}}"
    ]
  }
}}

Then, to search by poster (image URL):

// POST /indexes/$INDEX_NAME/search
{
  "media": {
    "poster": "https://image.tmdb.org/t/p/w500/6FfCtAuVAW8XJjZ7eWeLibRLWTw.jpg"
  },
  "hybrid": {
    "embedder": "voyage",
    // semanticRatio is specified here for explicitness,
    // but it would default to 1.0 if omitted, as we don't have a text query
    // (no "q") here.
    "semanticRatio": 1.0
  }
}

To search by image data (encoded as base64):

// POST /indexes/$INDEX_NAME/search

{
  "media": {
    "image": {
      "mime": "image/jpeg",
      // image bytes encoded as base64
      "data": "/9j/4AAQSkZJRgABAQAAAQABAAD/2w..."
    }
  "hybrid": {
    "embedder": "voyage",
  }
}

To search by text (performing a hybrid search):

// POST /indexes/$INDEX_NAME/search
{
  // note that since the model is multimodal, this will also compare the produced embedding
  // with the poster images from movie, so describing a poster image here could find the
  // associated movie
  "q": "A movie with lightsabers in space",
  "hybrid": {
    "embedder": "voyage",
    "semanticRatio": 0.5
  }
}

For more information about this feature, please refer to its public usage page

Done by @dureuill in #5596

Export/Transfer documents between instances

Meilisearch now allows to transfer documents from an instance to another without having to create a dump or a snapshot. This feature will ease the migration to the Cloud by streaming all the documents from a local instance to a cloud one.

Usage

We expose one single route on the instance which accepts a bunch of parameters.

  • url: Where do we want to send our settings and documents.
  • apiKey: (optional) The API key to have the rights to send those requests. Usually the master key of the remote machine.
  • payloadSize: (optional) A human readable size defining the size of the payloads to send. Defaults to 50 MiB.
  • indexes: (optional) A set of patterns of matching the indexes you want to export. If not defined, defaults to all indexes without filter.
  • filter: (optional) A filter defining the subset of documents to actually export.
  • overrideSettings: (optional, default false) When false, will not set settings nor update the primary key on indexes that already exist on the remote instance. Will still send the documents

POST: /export

{
  "url": "http://localhost:7711",
  "apiKey": null,
  "payloadSize": "123 MiB",
  "indexes": {
    "*": {
      "filter": null,
      "overrideSettings": true
    }
  }
}

Response:

{
  "taskUid": 2,
  "indexUid": null,
  "status": "enqueued",
  "type": "export",
  "enqueuedAt": "2025-06-26T12:54:10.785864Z"
}

One task can retry requests in case of failure (not indefinitely, though) and can also be canceled. However, keep in mind that canceling this export task will not cancel tasks received by the targeted Meilisearch instance.

Done by @kerollmops with the help of @mubelotix in #5670

Other improvements

Fixes 🐞

Misc

❤️ Thanks again to our external contributors:

  • Meilisearch: @martin-g, @lblack00, @mcmah309, @nnethercott, @arthurgousset, @Mubelotix, @diksipav, @Nymuxyzo
  • Arroy: @nnethercott

下载链接