从 Elasticsearch 查询迁移到 Milvus
基于 Apache Lucene 构建的 Elasticsearch 是领先的开源搜索引擎。然而,它在现代人工智能应用中面临着各种挑战,包括更新成本高、实时性差、分片管理效率低、非云原生设计以及资源需求过高。作为云原生向量数据库,Milvus 通过解耦存储和计算、高效的高维数据索引以及与现代基础设施的无缝集成,克服了这些问题。它为人工智能工作负载提供了卓越的性能和可扩展性。
本文旨在帮助您将代码库从 Elasticsearch 迁移到 Milvus,并提供中间转换查询的各种示例。
概述
在 Elasticsearch 中,查询上下文中的操作会生成相关性分数,而过滤器上下文中的操作不会生成相关性分数。同样,Milvus 的搜索会产生相似性得分,而类似过滤器的查询则不会。将代码库从 Elasticsearch 迁移到 Milvus 时,关键原则是将 Elasticsearch 查询上下文中使用的字段转换为向量字段,以便生成相似性得分。
下表列出了一些 Elasticsearch 查询模式及其在 Milvus 中的对应模式。
Elasticsearch 查询 | Milvus 对应模式 | 备注 |
|---|---|---|
全文查询 | ||
全文搜索 | 两者提供类似的功能。 | |
词项级查询 | ||
| 在过滤器上下文中使用这些 Elasticsearch 查询时,两者都能提供相同或类似的功能。 | |
| ||
比较运算符,如 | ||
比较运算符,如 | ||
| ||
| ||
逻辑运算符,如 | 在过滤器上下文中使用时,这两种运算符都能提供类似的功能。 | |
向量查询 | ||
向量搜索 | Milvus 提供更高级的向量搜索功能。 | |
混合搜索 | Milvus 支持多种 Reranker 策略。 | |
全文查询
在 Elasticsearch 中,全文查询使您能够搜索分析过的文本字段,如电子邮件正文。查询字符串将使用索引过程中应用于字段的相同分析器进行处理。
匹配查询
在 Elasticsearch 中,匹配查询会返回与所提供的文本、数字、日期或布尔值相匹配的文档。在匹配之前会对所提供的文本进行分析。
下面是一个使用匹配查询的 Elasticsearch 搜索请求示例。
resp = client.search(
query={
"match": {
"message": {
"query": "this is a test"
}
}
},
)
Milvus 通过全文搜索功能提供了相同的功能。你可以按如下方式将上述 Elasticsearch 查询转换为 Milvus 查询:
res = client.search(
collection_name="my_collection",
data=["this is a test"],
anns_field="message_sparse",
output_fields=["id", "message"]
)
在上面的示例中,message_sparse 是一个稀疏向量字段,由名为 message 的 VarChar 字段衍生而来。Milvus 使用 BM25 Embedding 模型将 message 字段中的值转换为稀疏 Embedding,并将其存储在 message_sparse 字段中。收到搜索请求后,Milvus 会使用相同的 BM25 模型嵌入纯文本查询有效载荷,并执行稀疏向量搜索,然后返回 output_fields 参数中指定的 id 和 message 字段以及相应的相似性分数。
要使用此功能,必须在 message 字段上启用分析器,并定义一个函数从中导出 message_sparse 字段。有关启用分析器和在 Milvus 中创建派生函数的详细说明,请参阅 全文搜索。
词项级查询
在 Elasticsearch 中,词项级查询用于根据结构化数据中的精确值查找文档,如日期范围、IP 地址、价格或产品 ID。本节概述了一些 Elasticsearch 词项级查询在 Milvus 中的可能等价形式。为了与 Milvus 的功能保持一致,本节中的所有示例都在过滤器上下文中进行了运算符调整。
ID
在 Elasticsearch 中,你可以在过滤器上下文中根据 ID 查找文件,如下所示:
resp = client.search(
query={
"bool": {
"filter": {
"ids": {
"values": [
"1",
"4",
"100"
]
}
}
}
},
)
在 Milvus 中,你也可以根据 ID 查找实体,如下所示:
# Use the filter parameter
res = client.query(
collection_name="my_collection",
filter="id in [1, 4, 100]",
output_fields=["id", "title"]
)
# Use the ids parameter
res = client.query(
collection_name="my_collection",
ids=[1, 4, 100],
output_fields=["id", "title"]
)
你可以在 本页 找到 Elasticsearch 示例。有关查询和获取请求以及 Milvus 中过滤器表达式的详细信息,请参阅 查询 和 过滤。
前缀查询
在 Elasticsearch 中,你可以在过滤器上下文中查找在所提供字段中包含特定前缀的文档,如下所示:
resp = client.search(
query={
"bool": {
"filter": {
"prefix": {
"user": {
"value": "ki"
}
}
}
}
},
)
在 Milvus 中,你可以按如下方式查找其值以指定前缀开头的实体:
res = client.query(
collection_name="my_collection",
filter='user like "ki%"',
output_fields=["id", "user"]
)
你可以在 本页 找到 Elasticsearch 示例。有关 Milvus 中 like 运算符的详细信息,请参阅 使用 LIKE 进行模式匹配。
范围查询
在 Elasticsearch 中,您可以查找包含所提供范围内术语的文档,如下所示:
resp = client.search(
query={
"bool": {
"filter": {
"range": {
"age": {
"gte": 10,
"lte": 20
}
}
}
}
},
)
在 Milvus 中,你可以按如下方式查找特定字段中的值在所提供范围内的实体:
res = client.query(
collection_name="my_collection",
filter='10 <= age <= 20',
output_fields=["id", "user", "age"]
)
你可以在 本页 找到 Elasticsearch 示例。有关 Milvus 中比较运算符的详细信息,请参阅 比较运算符。
term 查询
在 Elasticsearch 中,你可以查找在所提供字段中包含 精确 术语的文档,如下所示:
resp = client.search(
query={
"bool": {
"filter": {
"term": {
"status": {
"value": "retired"
}
}
}
}
},
)
在 Milvus 中,您可以按如下方式查找指定字段中的值正好是指定术语的实体:
# use ==
res = client.query(
collection_name="my_collection",
filter='status=="retired"',
output_fields=["id", "user", "status"]
)
# use TEXT_MATCH
res = client.query(
collection_name="my_collection",
filter='TEXT_MATCH(status, "retired")',
output_fields=["id", "user", "status"]
)
你可以在 本页 找到 Elasticsearch 示例。有关 Milvus 中比较运算符的详细信息,请参阅 比较运算符。
terms 查询
在 Elasticsearch 中,你可以查找在所提供字段中包含一个或多个 精确 术语的文档,如下所示:
resp = client.search(
query={
"bool": {
"filter": {
"terms": {
"degree": [
"graduate",
"post-graduate"
]
}
}
}
}
)
Milvus 没有与 terms 查询完全等价的单一操作,但可以查找指定字段值属于给定词项集合的实体:
# use in
res = client.query(
collection_name="my_collection",
filter='degree in ["graduate", "post-graduate"]',
output_fields=["id", "user", "degree"]
)
# use TEXT_MATCH
res = client.query(
collection_name="my_collection",
filter='TEXT_MATCH(degree, "graduate post-graduate")',
output_fields=["id", "user", "degree"]
)
您可以在 本页 找到 Elasticsearch 示例。有关 Milvus 中范围运算符的详细信息,请参阅 范围运算符。
通配符查询
在 Elasticsearch 中,你可以查找包含与通配符模式匹配的术语的文档,如下所示:
resp = client.search(
query={
"bool": {
"filter": {
"wildcard": {
"user": {
"value": "ki*y"
}
}
}
}
},
)
Milvus 在过滤条件中不支持通配符。不过,你可以使用 like 运算符来实现类似的效果,如下所示:
res = client.query(
collection_name="my_collection",
filter='user like "ki%" AND user like "%y"',
output_fields=["id", "user"]
)
Elasticsearch 示例参见 wildcard 查询。有关 Milvus LIKE 运算符的详细信息,请参阅 使用 LIKE 进行模式匹配。
布尔查询
在 Elasticsearch 中,布尔查询是指匹配与其他查询的布尔组合相匹配的文档的查询。
下面的示例改编自 本页 Elasticsearch 文档中的一个示例。该查询将返回名称中包含 kimchy 的用户,并带有 production 标记。
resp = client.search(
query={
"bool": {
"filter": [
{
"term": {
"user": "kimchy"
}
},
{
"term": {
"tags": "production"
}
}
]
}
},
)
在 Milvus 中,你可以做类似的事情,如下所示:
res = client.query(
collection_name="my_collection",
filter='user like "%kimchy%" AND ARRAY_CONTAINS(tags, "production")',
output_fields=["id", "user", "age", "tags"]
)
上面的示例假定在目标 Collection 中有一个 VarChar 类型的 user 字段和一个 Array 类型的 tags 字段。查询将返回名称中包含 kimchy 的用户,并带有 production 标记。
向量查询
在 Elasticsearch 中,向量查询是对向量字段进行处理以有效执行语义搜索的专门查询。
Knn 查询
Elasticsearch 支持近似 kNN 查询和精确、暴力 kNN 查询。你可以用这两种方式找到与查询向量最近的k 个向量,以相似度指标来衡量,具体如下:
resp = client.search(
index="my-image-index",
size=3,
query={
"knn": {
"field": "image-vector",
"query_vector": [
-5,
9,
-12
],
"k": 10
}
},
)
作为专门的向量数据库,Milvus 使用索引类型来优化向量搜索。通常,它优先考虑对高维向量数据进行近似近邻(ANN)搜索。虽然使用 FLAT 索引类型的暴力 kNN 搜索能得到精确的结果,但它既耗时又耗资源。相比之下,使用 AUTOINDEX 或其他索引类型的 ANN 搜索能在速度和精确度之间取得平衡,其性能明显比 kNN 更快、更节省资源。
在 Milvus 中,与上述向量查询类似的等价关系是这样的:
res = client.search(
collection_name="my_collection",
anns_field="image-vector",
data=[[-5, 9, -12]],
limit=10
)
您可以在 本页 找到 Elasticsearch 示例。有关 Milvus 中 ANN 搜索的详细信息,请阅读 基本 ANN 搜索。
RRF(Reciprocal Rank Fusion(RRF))
Elasticsearch 提供 RRF(Reciprocal Rank Fusion,Reciprocal Rank Fusion(RRF)),用于将具有不同相关性指标的多个结果集合并为一个统一的排序结果集。
以下示例将传统的词项搜索与 kNN 向量搜索结合,以提升搜索相关性:
client.search(
index="my_index",
size=10,
retriever={
"rrf": {
"retrievers": [
{
"standard": {
"query": {
"term": {
"text": "shoes"
}
}
}
},
{
"knn": {
"field": "vector",
"query_vector": [1.25, 2, 3.5],
"k": 50,
"num_candidates": 100
}
}
],
"rank_window_size": 50,
"rank_constant": 20
}
},
)
在此示例中,RRF 合并两个检索器的结果:
-
标准词项搜索:查找
text字段中包含词项"shoes"的文档。 -
kNN 搜索:使用给定查询向量搜索
vector字段。
每个检索器最多提供 50 个最匹配结果,RRF 对这些结果进行重排序,并返回最终的前 10 个结果。
在 Milvus 中,可以组合多个向量字段上的搜索、应用重排序策略,并从合并结果中返回 top-K,从而实现类似的混合搜索。Milvus 同时支持 RRF 和加权重排序策略。有关详细信息,请参阅 重排序。
以下代码是在 Milvus 中实现上述 Elasticsearch 示例的近似等价方式。
from pymilvus import AnnSearchRequest, RRFRanker
dense_request = AnnSearchRequest(
data=[[1.25, 2, 3.5]],
anns_field="vector",
param={
"metric_type": "IP",
"params": {"nprobe": 10},
},
limit=100,
)
sparse_request = AnnSearchRequest(
data=["shoes"],
anns_field="text_sparse",
param={"metric_type": "BM25"},
limit=100,
)
res = client.hybrid_search(
collection_name="my_collection",
reqs=[dense_request, sparse_request],
reranker=RRFRanker(),
limit=10,
)
该示例展示了 Milvus 混合搜索如何组合以下两类搜索:
-
稠密向量搜索:使用内积(IP)度量,将
nprobe设置为 10,在vector字段上执行近似最近邻(ANN)搜索。 -
稀疏向量搜索:在
text_sparse字段上使用 BM25 相似度度量。
两类搜索分别执行,其结果在合并后由 RRF Ranker 进行重排序。混合搜索从重排序后的列表中返回前 10 个实体。
Elasticsearch 的 RRF 合并标准文本查询与 kNN 搜索的结果;Milvus 则合并稀疏向量搜索与稠密向量搜索的结果,提供针对多模态数据优化的混合搜索能力。
回顾
在本文中,我们介绍了将典型 Elasticsearch 查询转换为 Milvus 对应查询的方法,包括词项级查询、布尔查询、全文查询和向量查询。如果您对其他 Elasticsearch 查询的转换有进一步的问题,请随时联系我们。