跳到主要内容
版本:v2.4.x

单向量搜索

插入数据后,下一步就是在 Milvus 中对 Collection 进行相似性搜索。

根据 Collection 中向量字段的数量,Milvus 允许您进行两种类型的搜索:

  • 单向量搜索:如果您的 Collection 只有一个向量字段,请使用 search()方法来查找最相似的实体。该方法会将您的查询向量与 Collection 中的现有向量进行比较,并返回最匹配的 ID 以及它们之间的距离。作为选项,它还可以返回结果的向量值和元数据。
  • 混合搜索:对于有两个或更多向量字段的 Collection,可使用 hybrid_search()方法。该方法会执行多个近似近邻(ANN)搜索请求,并在 Reranking 后将结果组合起来,返回最相关的匹配结果。

本指南主要介绍如何在 Milvus 中执行单向量搜索。有关混合搜索的详细信息,请参阅混合搜索

搜索概述

有多种搜索类型可满足不同需求:

  • 基本搜索:包括单向量搜索、批量向量搜索、Partition 搜索和指定输出字段搜索。

  • 过滤搜索:根据标量字段应用过滤条件,以完善搜索结果。

  • 范围搜索查找与查询向量在特定距离范围内的向量。

  • 分组搜索:根据特定字段对搜索结果进行分组,以确保搜索结果的多样性。

准备工作

下面的代码片段对现有代码进行了重新利用,以建立与 Milvus 的连接并快速设置 Collection。

from pymilvus import MilvusClient
import random

# 1. Set up a Milvus client
client = MilvusClient(
uri="http://localhost:19530"
)

# 2. Create a collection
client.create_collection(
collection_name="quick_setup",
dimension=5,
metric_type="IP"
)

# 3. Insert randomly generated vectors
colors = ["green", "blue", "yellow", "red", "black", "white", "purple", "pink", "orange", "brown", "grey"]
data = []

for i in range(1000):
current_color = random.choice(colors)
data.append({
"id": i,
"vector": [ random.uniform(-1, 1) for _ in range(5) ],
"color": current_color,
"color_tag": f"{current_color}_{str(random.randint(1000, 9999))}"
})

res = client.insert(
collection_name="quick_setup",
data=data
)

print(res)

# Output
#
# {
# "insert_count": 1000,
# "ids": [
# 0,
# 1,
# 2,
# 3,
# 4,
# 5,
# 6,
# 7,
# 8,
# 9,
# "(990 more items hidden)"
# ]
# }

# 6.1 Create partitions
client.create_partition(
collection_name="quick_setup",
partition_name="red"
)

client.create_partition(
collection_name="quick_setup",
partition_name="blue"
)

# 6.1 Insert data into partitions
red_data = [ {"id": i, "vector": [ random.uniform(-1, 1) for _ in range(5) ], "color": "red", "color_tag": f"red_{str(random.randint(1000, 9999))}" } for i in range(500) ]
blue_data = [ {"id": i, "vector": [ random.uniform(-1, 1) for _ in range(5) ], "color": "blue", "color_tag": f"blue_{str(random.randint(1000, 9999))}" } for i in range(500) ]

res = client.insert(
collection_name="quick_setup",
data=red_data,
partition_name="red"
)

print(res)

# Output
#
# {
# "insert_count": 500,
# "ids": [
# 0,
# 1,
# 2,
# 3,
# 4,
# 5,
# 6,
# 7,
# 8,
# 9,
# "(490 more items hidden)"
# ]
# }

res = client.insert(
collection_name="quick_setup",
data=blue_data,
partition_name="blue"
)

print(res)

# Output
#
# {
# "insert_count": 500,
# "ids": [
# 0,
# 1,
# 2,
# 3,
# 4,
# 5,
# 6,
# 7,
# 8,
# 9,
# "(490 more items hidden)"
# ]
# }

在发送search 请求时,您可以提供一个或多个代表查询嵌入的向量值,以及表示要返回结果数量的limit 值。

根据您的数据和查询向量,您可能会得到少于limit 的结果。当limit 大于您查询的可能匹配向量数时,就会出现这种情况。

在 Milvus 中,单向量搜索是最简单的search 操作符,用于查找与给定查询向量最相似的向量。

要执行单向量搜索,请指定目标 Collection 名称、查询向量和所需结果数 (limit)。该操作会返回一个结果集,其中包括最相似的向量、它们的 ID 和与查询向量的距离。

下面是搜索与查询向量最相似的前 5 个实体的示例:

# Single vector search
res = client.search(
collection_name="quick_setup", # Replace with the actual name of your collection
# Replace with your query vector
data=[[0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592]],
limit=5, # Max. number of search results to return
search_params={"metric_type": "IP", "params": {}} # Search parameters
)

# Convert the output to a formatted JSON string
result = json.dumps(res, indent=4)
print(result)
参数说明
collection_name现有 Collection 的名称。
data一个向量 Embedding 列表。
Milvus 会搜索与指定向量 Embedding 最相似的向量 Embedding。
limit要返回的实体总数。
可以将此参数与param中的偏移量结合使用,以启用分页。
此值与param中的偏移量之和应小于 16,384。
search_params该操作符特有的参数设置。
  • metric_type:适用于该操作的度量类型。应与上面指定的向量字段索引时使用的类型相同。可能的值有L2IPCOSINEJACCARDHAMMING
  • params:附加参数。详情请参阅search()

|

参数说明
collectionName现有 Collection 的名称。
data一个 Embedding 列表。
Milvus 会搜索与指定 Embedding 最相似的 Embedding。
topK搜索结果中要返回的记录数。该参数使用与limit参数相同的语法,因此只需设置其中一个。
可以将该参数与param中的偏移量结合使用,以启用分页。
该值与param中的偏移量之和应小于 16,384。
参数说明
collection_name现有 Collection 的名称。
data一个向量 Embedding 列表。
Milvus 会搜索与指定向量 Embedding 最相似的向量 Embedding。
limit要返回的实体总数。
可以将此参数与param中的偏移量结合使用,以启用分页。
此值与param中的偏移量之和应小于 16,384。

输出结果类似于下图:

[
[
{
"id": 0,
"distance": 1.4093276262283325,
"entity": {}
},
{
"id": 4,
"distance": 0.9902134537696838,
"entity": {}
},
{
"id": 1,
"distance": 0.8519943356513977,
"entity": {}
},
{
"id": 5,
"distance": 0.7972343564033508,
"entity": {}
},
{
"id": 2,
"distance": 0.5928734540939331,
"entity": {}
}
]
]

输出结果会显示与您的查询向量最接近的前 5 个邻居,包括它们的唯一 ID 和计算出的距离。

批量向量搜索扩展了单向量搜索的概念,允许在单个请求中搜索多个查询向量。这种类型的搜索非常适合需要为一组查询向量查找相似向量的场景,大大减少了所需的时间和计算资源。

在批量向量搜索中,您可以在data 字段中包含多个查询向量。系统会并行处理这些向量,为每个查询向量返回一个单独的结果集,每个结果集都包含在 Collection 中找到的最接近的匹配结果。

下面是一个从两个查询向量中搜索最相似实体的两个不同 Collection 的示例:

# Bulk-vector search
res = client.search(
collection_name="quick_setup", # Replace with the actual name of your collection
data=[
[0.19886812562848388, 0.06023560599112088, 0.6976963061752597, 0.2614474506242501, 0.838729485096104],
[0.3172005263489739, 0.9719044792798428, -0.36981146090600725, -0.4860894583077995, 0.95791889146345]
], # Replace with your query vectors
limit=2, # Max. number of search results to return
search_params={"metric_type": "IP", "params": {}} # Search parameters
)

result = json.dumps(res, indent=4)
print(result)

输出结果类似于以下内容:

[
[
{
"id": 1,
"distance": 1.3017789125442505,
"entity": {}
},
{
"id": 7,
"distance": 1.2419954538345337,
"entity": {}
}
], # Result set 1
[
{
"id": 3,
"distance": 2.3358664512634277,
"entity": {}
},
{
"id": 8,
"distance": 0.5642921924591064,
"entity": {}
}
] # Result set 2
]

结果包括两组最近邻,每个查询向量一组,展示了批量向量搜索同时处理多个查询向量的效率。

Partition 搜索可将搜索范围缩小到 Collection 的特定子集或 Partition。这对于数据被分割成逻辑或分类的有组织数据集特别有用,可以通过减少要扫描的数据量来加快搜索操作。

要进行 Partition 搜索,只需在partition_names 搜索请求中包含目标 Partition 的名称即可。这就指定了search 操作只考虑指定 Partition 内的向量。

下面是在red 中搜索实体的示例:

# 6.2 Search within a partition
query_vector = [0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592]

res = client.search(
collection_name="quick_setup",
data=[query_vector],
limit=5,
search_params={"metric_type": "IP", "params": {"level": 1}},
partition_names=["red"]
)

print(res)

输出结果类似于以下内容:

[
[
{
"id": 16,
"distance": 0.9200337529182434,
"entity": {}
},
{
"id": 14,
"distance": 0.4505271911621094,
"entity": {}
},
{
"id": 15,
"distance": 0.19924677908420563,
"entity": {}
},
{
"id": 17,
"distance": 0.0075093843042850494,
"entity": {}
},
{
"id": 13,
"distance": -0.14609718322753906,
"entity": {}
}
]
]

然后,在blue 中搜索实体:

res = client.search(
collection_name="quick_setup",
data=[query_vector],
limit=5,
search_params={"metric_type": "IP", "params": {"level": 1}},
partition_names=["blue"]
)

print(res)

输出结果类似于以下内容:

[
[
{
"id": 20,
"distance": 2.363696813583374,
"entity": {}
},
{
"id": 26,
"distance": 1.0665391683578491,
"entity": {}
},
{
"id": 23,
"distance": 1.066049575805664,
"entity": {}
},
{
"id": 29,
"distance": 0.8353596925735474,
"entity": {}
},
{
"id": 28,
"distance": 0.7484277486801147,
"entity": {}
}
]
]

red 中的数据与blue 中的数据不同。因此,搜索结果将受限于指定的 Partition,以反映该子集的独特特征和数据分布。

使用输出字段搜索

使用输出字段搜索允许您指定匹配向量的哪些属性或字段应包含在搜索结果中。

您可以在请求中指定output_fields ,以返回包含特定字段的结果。

下面是一个使用color 属性值返回结果的示例:

# Search with output fields
res = client.search(
collection_name="quick_setup", # Replace with the actual name of your collection
data=[[0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592]],
limit=5, # Max. number of search results to return
search_params={"metric_type": "IP", "params": {}}, # Search parameters
output_fields=["color"] # Output fields to return
)

result = json.dumps(res, indent=4)
print(result)

输出结果类似于以下内容:

[
[
{
"id": 0,
"distance": 1.4093276262283325,
"entity": {
"color": "pink_8682"
}
},
{
"id": 16,
"distance": 1.0159327983856201,
"entity": {
"color": "yellow_1496"
}
},
{
"id": 4,
"distance": 0.9902134537696838,
"entity": {
"color": "red_4794"
}
},
{
"id": 14,
"distance": 0.9803846478462219,
"entity": {
"color": "green_2899"
}
},
{
"id": 1,
"distance": 0.8519943356513977,
"entity": {
"color": "red_7025"
}
}
]
]

除了近邻,搜索结果还将包括指定字段color ,为每个匹配向量提供更丰富的信息。

过滤搜索将标量过滤器应用到向量搜索中,允许你根据特定条件对搜索结果进行细化。有关过滤表达式的更多信息,请参阅布尔表达式规则获取与标量查询中的示例。

使用like 操作符

like 操作符通过评估包括前缀、后缀和后缀在内的模式来增强字符串搜索:

  • 前缀匹配:要查找以特定前缀开头的值,请使用语法'like "prefix%"'
  • 后缀匹配:要查找字符串中包含特定字符序列的值,请使用语法'like "%infix%"'
  • 后缀匹配:要查找以特定后缀结尾的值,请使用语法'like "%suffix"'

对于单字符匹配,下划线 (_) 可作为一个字符的通配符,如'like "y_llow"'

搜索字符串中的特殊字符

如果要搜索包含下划线 (_) 或百分号 (%) 等特殊字符的字符串,这些字符通常在搜索模式中用作通配符(_ 用于任何单个字符,% 用于任何字符序列),则必须将这些字符转义为字面字符。使用反斜线 (\) 转义特殊字符,并记住转义反斜线本身。例如

  • 要搜索字面下划线,请使用\\_
  • 要搜索百分号,请使用\\%

因此,如果要搜索文本"_version_" ,查询格式应为'like "\\_version\\_"' ,以确保下划线被视为搜索词的一部分,而不是通配符。

过滤颜色前缀为红色的结果:

# Search with filter
res = client.search(
collection_name="quick_setup", # Replace with the actual name of your collection
data=[[0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592]],
limit=5, # Max. number of search results to return
search_params={"metric_type": "IP", "params": {}}, # Search parameters
output_fields=["color"], # Output fields to return
filter='color like "red%"'
)

result = json.dumps(res, indent=4)
print(result)

输出结果类似于以下内容:

[
[
{
"id": 4,
"distance": 0.9902134537696838,
"entity": {
"color": "red_4794"
}
},
{
"id": 1,
"distance": 0.8519943356513977,
"entity": {
"color": "red_7025"
}
},
{
"id": 6,
"distance": -0.4113418459892273,
"entity": {
"color": "red_9392"
}
}
]
]

过滤颜色包含字母ll的结果:

# Infix match on color field
res = client.search(
collection_name="quick_setup", # Replace with the actual name of your collection
data=[[0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592]],
limit=5, # Max. number of search results to return
search_params={"metric_type": "IP", "params": {}}, # Search parameters
output_fields=["color"], # Output fields to return
filter='color like "%ll%"' # Filter on color field, infix match on "ll"
)

result = json.dumps(res, indent=4)
print(result)

输出结果类似于以下内容:

[
[
{
"id": 5,
"distance": 0.7972343564033508,
"entity": {
"color": "yellow_4222"
}
}
]
]

范围搜索可以查找与查询向量在指定距离范围内的向量。

通过设置radius 和可选的range_filter ,您可以调整搜索的广度,将与查询向量有些相似的向量包括在内,从而更全面地查看潜在的匹配结果。

  • radius:定义搜索空间的外部边界。只有与查询向量距离在此范围内的向量才会被视为潜在匹配向量。

  • range_filter:radius 设置搜索的外部界限,而range_filter 则可用于定义内部界限,创建一个距离范围,向量必须在该范围内才会被视为匹配。

# Conduct a range search
search_params = {
"metric_type": "IP",
"params": {
"radius": 0.8, # Radius of the search circle
"range_filter": 1.0 # Range filter to filter out vectors that are not within the search circle
}
}

res = client.search(
collection_name="quick_setup", # Replace with the actual name of your collection
data=[[0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592]],
limit=3, # Max. number of search results to return
search_params=search_params, # Search parameters
output_fields=["color"], # Output fields to return
)

result = json.dumps(res, indent=4)
print(result)

输出结果类似于以下内容:

[
[
{
"id": 4,
"distance": 0.9902134537696838,
"entity": {
"color": "red_4794"
}
},
{
"id": 14,
"distance": 0.9803846478462219,
"entity": {
"color": "green_2899"
}
},
{
"id": 1,
"distance": 0.8519943356513977,
"entity": {
"color": "red_7025"
}
}
]
]

您会发现,所有返回的实体与查询向量的距离都在 0.8 到 1.0 之间。

radiusrange_filter 的参数设置因使用的度量类型而异。

度量类型特征搜索范围设置
L2L2 距离越小表示相似度越高。要从结果中排除最接近的向量,请确保:
range_filter <= distance <radius
IPIP 距离越大,表示相似度越高。要从结果中排除最接近的向量,请确保:
radius < distance <=range_filter
COSINE余弦值越大,表示相似度越高。要从结果中排除最接近的向量,请确保:
radius < distance <=range_filter
JACCARDJaccard 距离越小,表示相似度越高。要从结果中排除最接近的向量,请确保:
range_filter <= 距离 <radius
HAMMING汉明距离越小,相似度越高。要从结果中排除最接近的向量,请确保:
range_filter <= distance <radius

要了解有关距离度量类型的更多信息,请参阅相似度量

在 Milvus 中,分组搜索旨在提高搜索结果的全面性和准确性。

考虑 RAG 中的一种情况,即文档负载被分成不同的段落,每个段落由一个 Embedding 表示。用户希望找到最相关的段落,以准确地提示 LLMs。普通的 Milvus 搜索功能可以满足这一要求,但它可能会导致搜索结果高度倾斜和偏差:大部分段落只来自少数几个文档,搜索结果的全面性非常差。这可能会严重影响词法管理器给出结果的准确性甚至正确性,并对词法管理器用户的使用体验造成负面影响。

分组搜索可以有效解决这一问题。通过传递group_by_field ,Milvus 用户可以将搜索结果分成若干组。这一功能可以大大提高搜索结果的全面性和公平性,明显改善 LLM 的输出质量。

以下是按字段对搜索结果进行分组的示例代码:

# Connect to Milvus
client = MilvusClient(uri='http://localhost:19530') # Milvus server address

# Load data into collection
client.load_collection("group_search") # Collection name

# Group search results
res = client.search(
collection_name="group_search", # Collection name
data=[[0.14529211512077012, 0.9147257273453546, 0.7965055218724449, 0.7009258593102812, 0.5605206522382088]], # Query vector
search_params={
"metric_type": "L2",
"params": {"nprobe": 10},
}, # Search parameters
limit=5, # Max. number of groups to return
group_by_field="doc_id", # Group results by document ID
output_fields=["doc_id", "passage_id"]
)

# Retrieve the values in the `doc_id` column
doc_ids = [result['entity']['doc_id'] for result in res[0]]
passage_ids = [result['entity']['passage_id'] for result in res[0]]

print(doc_ids)
print(passage_ids)

输出结果类似于下面的内容:

["doc_11", "doc_11", "doc_7", "doc_7", "doc_3", "doc_3", "doc_2", "doc_2", "doc_8", "doc_8"]
[5, 10, 11, 10, 9, 6, 5, 4, 9, 2]

在给定的输出结果中,我们可以看到,每个文档都恰好检索到了两个段落,总共有 5 个文档 Collection 构成了结果。

为了便于比较,我们注释掉与组相关的参数,然后进行常规搜索:

# Connect to Milvus
client = MilvusClient(uri='http://localhost:19530') # Milvus server address

# Load data into collection
client.load_collection("group_search") # Collection name

# Search without `group_by_field`
res = client.search(
collection_name="group_search", # Collection name
data=query_passage_vector, # Replace with your query vector
search_params={
"metric_type": "L2",
"params": {"nprobe": 10},
}, # Search parameters
limit=5, # Max. number of search results to return
# group_by_field="doc_id", # Group results by document ID
output_fields=["doc_id", "passage_id"]
)

# Retrieve the values in the `doc_id` column
doc_ids = [result['entity']['doc_id'] for result in res[0]]
passage_ids = [result['entity']['passage_id'] for result in res[0]]

print(doc_ids)
print(passage_ids)

输出结果类似于下面的内容:

["doc_11", "doc_11", "doc_11", "doc_11", "doc_11"]
[1, 10, 3, 12, 9]

在给定的输出中,可以观察到 "doc_11 "完全占据了搜索结果,盖过了其他文档的高质量段落,这可能是对 LLM 的不良提示。

局限性

  • 索引:此分组功能仅适用于使用这些索引类型编制索引的 Collection:flativf_flativf_sq8hnswdiskannsparse_inverted_index

  • 向量目前,分组搜索不支持BINARY_VECTOR类型的向量字段。有关数据类型的更多信息,请参阅支持的数据类型

  • 字段:目前,分组搜索只支持单列。无法在group_by_field 配置中指定多个字段名。 此外,分组搜索与 JSON、FLOAT、DOUBLE、ARRAY 或向量字段的数据类型不兼容。

  • 性能影响:请注意,性能会随着查询向量数的增加而降低。以具有 2 个 CPU 内核和 8 GB 内存的集群为例,分组搜索的执行时间会随着输入查询向量数量的增加而成正比增加。

  • 功能:目前,范围搜索搜索迭代器不支持分组搜索。

搜索参数

除范围搜索外,上述搜索均使用默认搜索参数。在正常情况下,无需手动设置搜索参数。

# In normal cases, you do not need to set search parameters manually
# Except for range searches.
search_parameters = {
'metric_type': 'L2',
'params': {
'nprobe': 10,
'level': 1
'radius': 1.0
'range_filter': 0.8
}
}

下表列出了搜索参数的所有可能设置。

参数名称参数描述
metric_type如何测量向量 Embedding 之间的相似性。
可能的值为IP,L2,COSINE,JACCARD, 和HAMMING ,默认值为已加载索引文件的值。
params.nprobe搜索时要查询的单元数。
取值范围为 [1,nlist[1]]。
params.level搜索精度级别。
可能的值为123 ,默认值为1 。值越高,结果越精确,但性能越差。
params.radius定义搜索空间的外部边界。只有与查询向量距离在此范围内的向量才会被视为潜在匹配。
值范围由metric_type 参数决定。例如,如果metric_type 设置为L2 ,则有效值范围为[0, ∞] 。如果metric_type 设置为COSINE ,则有效值范围为[-1, 1] 。更多信息,请参阅 "相似度指标"
params.range_filterradius 设置搜索的外部界限,而range_filter 可选择用于定义内部界限,创建一个距离范围,向量必须在该范围内才会被视为匹配。
值范围由metric_type 参数决定。例如,如果metric_type 设置为L2 ,则有效值范围为[0, ∞] 。如果metric_type 设置为COSINE ,则有效值范围为[-1, 1] 。更多信息,请参阅 "相似度指标"
说明

注释

[1] 索引后的群集单位数。索引 Collection 时,Milvus 会将向量数据细分为多个簇单元,其数量随实际索引设置而变化。

[2] 搜索中返回的实体数量。