单向量搜索
插入数据后,下一步就是在 Milvus 中对集合进行相似性搜索。
Milvus 允许您进行两种类型的搜索,具体取决于您的 Collections 中向量场的数量:
- 单向量搜索:如果您的 Collections 只有一个向量场,请使用
search()
方法来查找最相似的实体。该方法会将您的查询向量与集合中的现有向量进行比较,并返回最匹配的 ID 以及它们之间的距离。作为选项,它还可以返回结果的向量值和元数据。 - 混合搜索:对于有两个或更多向量字段的 Collections,可使用
hybrid_search()
方法。该方法会执行多个近似近邻(ANN)搜索请求,并在重新排序后将结果组合起来,返回最相关的匹配结果。
本指南主要介绍如何在 Milvus 中执行单向量搜索。有关混合搜索的详细信息,请参阅混合搜索。
搜索概述
有多种搜索类型可满足不同需求:
基本搜索:包括单矢量搜索、批量矢量搜索、分区搜索和指定输出字段搜索。
过滤搜索:根据标量字段应用过滤条件,以完善搜索结果。
范围搜索查找与查询向量在特定距离范围内的向量。
分组搜索:根据特定字段对搜索结果进行分组,以确保搜索结果的多样性。
准备工作
下面的代码片段对现有代码进行了重新利用,以建立与 Milvus 的连接并快速设置集合。
# 1. Set up a Milvus client
client = MilvusClient(
uri=CLUSTER_ENDPOINT,
token=TOKEN
)
# 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)"
# ]
# }
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Random;
import com.alibaba.fastjson.JSONObject;
import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.service.collection.request.CreateCollectionReq;
import io.milvus.v2.service.collection.request.GetLoadStateReq;
import io.milvus.v2.service.vector.request.InsertReq;
import io.milvus.v2.service.vector.response.InsertResp;
String CLUSTER_ENDPOINT = "http://localhost:19530";
// 1. Connect to Milvus server
ConnectConfig connectConfig = ConnectConfig.builder()
.uri(CLUSTER_ENDPOINT)
.build();
MilvusClientV2 client = new MilvusClientV2(connectConfig);
// 2. Create a collection in quick setup mode
CreateCollectionReq quickSetupReq = CreateCollectionReq.builder()
.collectionName("quick_setup")
.dimension(5)
.metricType("IP")
.build();
client.createCollection(quickSetupReq);
GetLoadStateReq loadStateReq = GetLoadStateReq.builder()
.collectionName("quick_setup")
.build();
boolean state = client.getLoadState(loadStateReq);
System.out.println(state);
// Output:
// true
// 3. Insert randomly generated vectors into the collection
List<String> colors = Arrays.asList("green", "blue", "yellow", "red", "black", "white", "purple", "pink", "orange", "brown", "grey");
List<JSONObject> data = new ArrayList<>();
for (int i=0; i<1000; i++) {
Random rand = new Random();
String current_color = colors.get(rand.nextInt(colors.size()-1));
JSONObject row = new JSONObject();
row.put("id", Long.valueOf(i));
row.put("vector", Arrays.asList(rand.nextFloat(), rand.nextFloat(), rand.nextFloat(), rand.nextFloat(), rand.nextFloat()));
row.put("color_tag", current_color + "_" + String.valueOf(rand.nextInt(8999) + 1000));
data.add(row);
}
InsertReq insertReq = InsertReq.builder()
.collectionName("quick_setup")
.data(data)
.build();
InsertResp insertResp = client.insert(insertReq);
System.out.println(JSONObject.toJSON(insertResp));
// Output:
// {"insertCnt": 1000}
// 6.1. Create a partition
CreatePartitionReq partitionReq = CreatePartitionReq.builder()
.collectionName("quick_setup")
.partitionName("red")
.build();
client.createPartition(partitionReq);
partitionReq = CreatePartitionReq.builder()
.collectionName("quick_setup")
.partitionName("blue")
.build();
client.createPartition(partitionReq);
// 6.2 Insert data into the partition
data = new ArrayList<>();
for (int i=1000; i<1500; i++) {
Random rand = new Random();
String current_color = "red";
JSONObject row = new JSONObject();
row.put("id", Long.valueOf(i));
row.put("vector", Arrays.asList(rand.nextFloat(), rand.nextFloat(), rand.nextFloat(), rand.nextFloat(), rand.nextFloat()));
row.put("color", current_color);
row.put("color_tag", current_color + "_" + String.valueOf(rand.nextInt(8999) + 1000));
data.add(row);
}
insertReq = InsertReq.builder()
.collectionName("quick_setup")
.data(data)
.partitionName("red")
.build();
insertResp = client.insert(insertReq);
System.out.println(JSONObject.toJSON(insertResp));
// Output:
// {"insertCnt": 500}
data = new ArrayList<>();
for (int i=1500; i<2000; i++) {
Random rand = new Random();
String current_color = "blue";
JSONObject row = new JSONObject();
row.put("id", Long.valueOf(i));
row.put("vector", Arrays.asList(rand.nextFloat(), rand.nextFloat(), rand.nextFloat(), rand.nextFloat(), rand.nextFloat()));
row.put("color", current_color);
row.put("color_tag", current_color + "_" + String.valueOf(rand.nextInt(8999) + 1000));
data.add(row);
}
insertReq = InsertReq.builder()
.collectionName("quick_setup")
.data(data)
.partitionName("blue")
.build();
insertResp = client.insert(insertReq);
System.out.println(JSONObject.toJSON(insertResp));
// Output:
// {"insertCnt": 500}
const { MilvusClient, DataType, sleep } = require("@zilliz/milvus2-sdk-node")
const address = "http://localhost:19530"
// 1. Set up a Milvus Client
client = new MilvusClient({address});
// 2. Create a collection in quick setup mode
await client.createCollection({
collection_name: "quick_setup",
dimension: 5,
metric_type: "IP"
});
// 3. Insert randomly generated vectors
const colors = ["green", "blue", "yellow", "red", "black", "white", "purple", "pink", "orange", "brown", "grey"]
data = []
for (let i = 0; i < 1000; i++) {
current_color = colors[Math.floor(Math.random() * colors.length)]
data.push({
id: i,
vector: [Math.random(), Math.random(), Math.random(), Math.random(), Math.random()],
color: current_color,
color_tag: `${current_color}_${Math.floor(Math.random() * 8999) + 1000}`
})
}
var res = await client.insert({
collection_name: "quick_setup",
data: data
})
console.log(res.insert_cnt)
// Output
//
// 1000
//
await client.createPartition({
collection_name: "quick_setup",
partition_name: "red"
})
await client.createPartition({
collection_name: "quick_setup",
partition_name: "blue"
})
// 6.1 Insert data into partitions
var red_data = []
var blue_data = []
for (let i = 1000; i < 1500; i++) {
red_data.push({
id: i,
vector: [Math.random(), Math.random(), Math.random(), Math.random(), Math.random()],
color: "red",
color_tag: `red_${Math.floor(Math.random() * 8999) + 1000}`
})
}
for (let i = 1500; i < 2000; i++) {
blue_data.push({
id: i,
vector: [Math.random(), Math.random(), Math.random(), Math.random(), Math.random()],
color: "blue",
color_tag: `blue_${Math.floor(Math.random() * 8999) + 1000}`
})
}
res = await client.insert({
collection_name: "quick_setup",
data: red_data,
partition_name: "red"
})
console.log(res.insert_cnt)
// Output
//
// 500
//
res = await client.insert({
collection_name: "quick_setup",
data: blue_data,
partition_name: "blue"
})
console.log(res.insert_cnt)
// Output
//
// 500
//
基本搜索
在发送search
请求时,您可以提供一个或多个代表查询嵌入的向量值,以及表示要返回结果数量的limit
值。
根据您的数据和查询向量,您可能会得到少于limit
的结果。当limit
大于查询可能匹配的向量数时,就会出现这种情况。
单向量搜索
在 Milvus 中,单向量搜索是最简单的search
操作符,用于查找与给定查询向量最相似的向量。
要执行单矢量搜索,请指定目标 Collections 名称、查询向量和所需结果数 (limit
)。该操作会返回一个结果集,其中包括最相似的向量、它们的 ID 和与查询向量的距离。
下面是搜索与查询向量最相似的前 5 个实体的示例:
# Single vector search
res = client.search(
collection_name="test_collection", # 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)
// 4. Single vector search
List<List<Float>> query_vectors = Arrays.asList(Arrays.asList(0.3580376395471989f, -0.6023495712049978f, 0.18414012509913835f, -0.26286205330961354f, 0.9029438446296592f));
SearchReq searchReq = SearchReq.builder()
.collectionName("quick_setup")
.data(query_vectors)
.topK(3) // The number of results to return
.build();
SearchResp searchResp = client.search(searchReq);
System.out.println(JSONObject.toJSON(searchResp));
// 4. Single vector search
var query_vector = [0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592],
res = await client.search({
collection_name: "quick_setup",
data: [query_vector],
limit: 3, // The number of results to return
})
console.log(res.results)
参数 | 说明 |
---|---|
collection_name |
现有 Collections 的名称。 |
data |
一个向量 embeddings 列表。 Milvus 会搜索与指定向量 embeddings 最相似的向量 embeddings。 |
limit |
要返回的实体总数。 可以将此参数与param中的偏移量结合使用,以启用分页。 此值与param中的偏移量之和应小于 16,384。 |
search_params |
该操作符特有的参数设置。
|
参数 | 说明 |
---|---|
collectionName |
现有 Collections 的名称。 |
data |
一个向量嵌入列表。 Milvus 会搜索与指定向量嵌入最相似的向量嵌入。 |
topK |
搜索结果中要返回的记录数。该参数使用与limit参数相同的语法,因此只需设置其中一个。 可以将该参数与param中的偏移量结合使用,以启用分页。 该值与param中的偏移量之和应小于 16,384。 |
参数 | 说明 |
---|---|
collection_name |
现有 Collection 的名称。 |
data |
一个向量 embeddings 列表。 Milvus 会搜索与指定向量 embeddings 最相似的向量 embeddings。 |
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": {}
}
]
]
{"searchResults": [[
{
"score": 1.263043,
"fields": {
"vector": [
0.9533119,
0.02538395,
0.76714665,
0.35481733,
0.9845762
],
"id": 740
}
},
{
"score": 1.2377806,
"fields": {
"vector": [
0.7411156,
0.08687937,
0.8254139,
0.08370924,
0.99095553
],
"id": 640
}
},
{
"score": 1.1869997,
"fields": {
"vector": [
0.87928146,
0.05324632,
0.6312755,
0.28005534,
0.9542448
],
"id": 455
}
}
]]}
[
{ score: 1.7463608980178833, id: '854' },
{ score: 1.744946002960205, id: '425' },
{ score: 1.7258622646331787, id: '718' }
]
输出结果会显示与您的查询向量最接近的前 5 个邻居,包括它们的唯一 ID 和计算出的距离。
批量向量搜索
批量向量搜索扩展了单向量搜索的概念,允许在单个请求中搜索多个查询向量。这种类型的搜索非常适合需要为一组查询向量查找相似向量的场景,大大减少了所需的时间和计算资源。
在批量向量搜索中,您可以在data
字段中包含多个查询向量。系统会并行处理这些向量,为每个查询向量返回一个单独的结果集,每个结果集都包含在 Collections 中找到的最接近的匹配结果。
下面是一个从两个查询向量中搜索最相似实体的两个不同集合的示例:
# Bulk-vector search
res = client.search(
collection_name="test_collection", # 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)
// 5. Batch vector search
query_vectors = Arrays.asList(
Arrays.asList(0.3580376395471989f, -0.6023495712049978f, 0.18414012509913835f, -0.26286205330961354f, 0.9029438446296592f),
Arrays.asList(0.19886812562848388f, 0.06023560599112088f, 0.6976963061752597f, 0.2614474506242501f, 0.838729485096104f)
);
searchReq = SearchReq.builder()
.collectionName("quick_setup")
.data(query_vectors)
.topK(2)
.build();
searchResp = client.search(searchReq);
System.out.println(JSONObject.toJSON(searchResp));
// 5. Batch vector search
var query_vectors = [
[0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592],
[0.19886812562848388, 0.06023560599112088, 0.6976963061752597, 0.2614474506242501, 0.838729485096104]
]
res = await client.search({
collection_name: "quick_setup",
data: query_vectors,
limit: 2,
})
console.log(res.results)
输出结果类似于以下内容:
[
[
{
"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
]
// Two sets of vectors are returned as expected
{"searchResults": [
[
{
"score": 1.263043,
"fields": {
"vector": [
0.9533119,
0.02538395,
0.76714665,
0.35481733,
0.9845762
],
"id": 740
}
},
{
"score": 1.2377806,
"fields": {
"vector": [
0.7411156,
0.08687937,
0.8254139,
0.08370924,
0.99095553
],
"id": 640
}
}
],
[
{
"score": 1.8654699,
"fields": {
"vector": [
0.4671427,
0.8378432,
0.98844475,
0.82763994,
0.9729997
],
"id": 638
}
},
{
"score": 1.8581753,
"fields": {
"vector": [
0.735541,
0.60140246,
0.86730254,
0.93152493,
0.98603314
],
"id": 855
}
}
]
]}
[
[
{ score: 2.3590476512908936, id: '854' },
{ score: 2.2896690368652344, id: '59' }
[
{ score: 2.664059638977051, id: '59' },
{ score: 2.59483003616333, id: '854' }
]
]
结果包括两组最近邻,每个查询向量一组,展示了批量向量搜索同时处理多个查询向量的效率。
分区搜索
分区搜索可将搜索范围缩小到集合的特定子集或分区。这对于数据被分割成逻辑或分类的有组织数据集特别有用,可以通过减少要扫描的数据量来加快搜索操作。
要进行分区搜索,只需在partition_names
搜索请求中包含目标分区的名称即可。这就指定了search
操作只考虑指定分区内的向量。
下面是在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)
// 6.3 Search within partitions
query_vectors = Arrays.asList(Arrays.asList(0.3580376395471989f, -0.6023495712049978f, 0.18414012509913835f, -0.26286205330961354f, 0.9029438446296592f));
searchReq = SearchReq.builder()
.collectionName("quick_setup")
.data(query_vectors)
.partitionNames(Arrays.asList("red"))
.topK(5)
.build();
searchResp = client.search(searchReq);
System.out.println(JSONObject.toJSON(searchResp));
// 6.2 Search within partitions
query_vector = [0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592]
res = await client.search({
collection_name: "quick_setup",
data: [query_vector],
partition_names: ["red"],
limit: 5,
})
console.log(res.results)
输出结果类似于以下内容:
[
[
{
"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": {}
}
]
]
{"searchResults": [
[
{
"score": 1.1677284,
"fields": {
"vector": [
0.9986977,
0.17964739,
0.49086612,
0.23155272,
0.98438674
],
"id": 1435
}
},
{
"score": 1.1476475,
"fields": {
"vector": [
0.6952647,
0.13417172,
0.91045254,
0.119336545,
0.9338931
],
"id": 1291
}
},
{
"score": 1.0969629,
"fields": {
"vector": [
0.3363194,
0.028906643,
0.6675426,
0.030419827,
0.9735209
],
"id": 1168
}
},
{
"score": 1.0741848,
"fields": {
"vector": [
0.9980543,
0.36063594,
0.66427994,
0.17359233,
0.94954175
],
"id": 1164
}
},
{
"score": 1.0584627,
"fields": {
"vector": [
0.7187005,
0.12674773,
0.987718,
0.3110777,
0.86093885
],
"id": 1085
}
}
],
[
{
"score": 1.8030131,
"fields": {
"vector": [
0.59726167,
0.7054632,
0.9573117,
0.94529945,
0.8664103
],
"id": 1203
}
},
{
"score": 1.7728865,
"fields": {
"vector": [
0.6672442,
0.60448086,
0.9325822,
0.80272985,
0.8861626
],
"id": 1448
}
},
{
"score": 1.7536311,
"fields": {
"vector": [
0.59663296,
0.77831805,
0.8578314,
0.88818026,
0.9030075
],
"id": 1010
}
},
{
"score": 1.7520742,
"fields": {
"vector": [
0.854198,
0.72294194,
0.9245805,
0.86126596,
0.7969224
],
"id": 1219
}
},
{
"score": 1.7452049,
"fields": {
"vector": [
0.96419,
0.943535,
0.87611496,
0.8268136,
0.79786557
],
"id": 1149
}
}
]
]}
[
{ score: 3.0258803367614746, id: '1201' },
{ score: 3.004319190979004, id: '1458' },
{ score: 2.880324363708496, id: '1187' },
{ score: 2.8246407508850098, id: '1347' },
{ score: 2.797295093536377, id: '1406' }
]
然后,在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)
searchReq = SearchReq.builder()
.collectionName("quick_setup")
.data(query_vectors)
.partitionNames(Arrays.asList("blue"))
.topK(5)
.build();
searchResp = client.search(searchReq);
System.out.println(JSONObject.toJSON(searchResp));
res = await client.search({
collection_name: "quick_setup",
data: [query_vector],
partition_names: ["blue"],
limit: 5,
})
console.log(res.results)
输出结果类似于以下内容:
[
[
{
"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": {}
}
]
]
{"searchResults": [
[
{
"score": 1.1628494,
"fields": {
"vector": [
0.7442872,
0.046407282,
0.71031404,
0.3544345,
0.9819991
],
"id": 1992
}
},
{
"score": 1.1470042,
"fields": {
"vector": [
0.5505825,
0.04367262,
0.9985836,
0.18922359,
0.93255126
],
"id": 1977
}
},
{
"score": 1.1450152,
"fields": {
"vector": [
0.89994013,
0.052991092,
0.8645576,
0.6406729,
0.95679337
],
"id": 1573
}
},
{
"score": 1.1439825,
"fields": {
"vector": [
0.9253267,
0.15890503,
0.7999555,
0.19126713,
0.898583
],
"id": 1552
}
},
{
"score": 1.1029172,
"fields": {
"vector": [
0.95661926,
0.18777144,
0.38115507,
0.14323527,
0.93137646
],
"id": 1823
}
}
],
[
{
"score": 1.8005109,
"fields": {
"vector": [
0.5953582,
0.7794224,
0.9388869,
0.79825854,
0.9197286
],
"id": 1888
}
},
{
"score": 1.7714822,
"fields": {
"vector": [
0.56805456,
0.89422905,
0.88187534,
0.914824,
0.8944365
],
"id": 1648
}
},
{
"score": 1.7561421,
"fields": {
"vector": [
0.83421993,
0.39865613,
0.92319834,
0.42695504,
0.96633124
],
"id": 1688
}
},
{
"score": 1.7553532,
"fields": {
"vector": [
0.89994013,
0.052991092,
0.8645576,
0.6406729,
0.95679337
],
"id": 1573
}
},
{
"score": 1.7543385,
"fields": {
"vector": [
0.16542226,
0.38248396,
0.9888778,
0.80913955,
0.9501492
],
"id": 1544
}
}
]
]}
[
{ score: 2.8421106338500977, id: '1745' },
{ score: 2.838560104370117, id: '1782' },
{ score: 2.8134000301361084, id: '1511' },
{ score: 2.718268871307373, id: '1679' },
{ score: 2.7014894485473633, id: '1597' }
]
red
中的数据与blue
中的数据不同。因此,搜索结果将受限于指定的分区,以反映该子集的独特特征和数据分布。
使用输出字段搜索
使用输出字段搜索允许您指定匹配向量的哪些属性或字段应包含在搜索结果中。
您可以在请求中指定output_fields
,以返回包含特定字段的结果。
下面是一个使用color
属性值返回结果的示例:
# Search with output fields
res = client.search(
collection_name="test_collection", # 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)
// 7. Search with output fields
query_vectors = Arrays.asList(Arrays.asList(0.3580376395471989f, -0.6023495712049978f, 0.18414012509913835f, -0.26286205330961354f, 0.9029438446296592f));
searchReq = SearchReq.builder()
.collectionName("quick_setup")
.data(query_vectors)
.outputFields(Arrays.asList("color"))
.topK(5)
.build();
searchResp = client.search(searchReq);
System.out.println(JSONObject.toJSON(searchResp));
// 7. Search with output fields
query_vector = [0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592]
res = await client.search({
collection_name: "quick_setup",
data: [query_vector],
limit: 5,
output_fields: ["color"],
})
console.log(res.results)
输出结果类似于以下内容:
[
[
{
"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"
}
}
]
]
{"searchResults": [
[
{
"score": 1.263043,
"fields": {}
},
{
"score": 1.2377806,
"fields": {}
},
{
"score": 1.1869997,
"fields": {}
},
{
"score": 1.1748955,
"fields": {}
},
{
"score": 1.1720343,
"fields": {}
}
]
]}
[
{ score: 3.036271572113037, id: '59', color: 'orange' },
{ score: 3.0267879962921143, id: '1745', color: 'blue' },
{ score: 3.0069446563720703, id: '854', color: 'black' },
{ score: 2.984386682510376, id: '718', color: 'black' },
{ score: 2.916019916534424, id: '425', color: 'purple' }
]
除了近邻,搜索结果还将包括指定字段color
,为每个匹配向量提供更丰富的信息。
过滤搜索
过滤搜索将标量过滤器应用到向量搜索中,允许你根据特定条件对搜索结果进行细化。有关过滤表达式的更多信息,请参阅布尔表达式规则和获取与标量查询中的示例。
使用like
操作符
like
操作符通过评估包括前缀、后缀和后缀在内的模式来增强字符串搜索:
- 前缀匹配:要查找以特定前缀开头的值,请使用语法
'like "prefix%"'
。 - 后缀匹配:要查找字符串中包含特定字符序列的值,请使用语法
'like "%infix%"'
。 - 后缀匹配:要查找以特定后缀结尾的值,请使用语法
'like "%suffix"'
。
对于单字符匹配,下划线 (_
) 可作为一个字符的通配符,如'like "y_llow"'
。
搜索字符串中的特殊字符
如果要搜索包含下划线 (_
) 或百分号 (%
) 等特殊字符的字符串,这些字符通常在搜索模式中用作通配符(_
用于任何单个字符,%
用于任何字符序列),则必须将这些字符转义为字面字符。使用反斜线 (\
) 转义特殊字符,并记住转义反斜线本身。例如
- 要搜索字面下划线,请使用
\\_
。 - 要搜索百分号,请使用
\\%
。
因此,如果要搜索文本"_version_"
,查询格式应为'like "\\_version\\_"'
,以确保下划线被视为搜索词的一部分,而不是通配符。
过滤颜色前缀为红色的结果:
# Search with filter
res = client.search(
collection_name="test_collection", # 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)
// 8. Filtered search
query_vectors = Arrays.asList(Arrays.asList(0.3580376395471989f, -0.6023495712049978f, 0.18414012509913835f, -0.26286205330961354f, 0.9029438446296592f));
searchReq = SearchReq.builder()
.collectionName("quick_setup")
.data(query_vectors)
.outputFields(Arrays.asList("color_tag"))
.filter("color_tag like \"red%\"")
.topK(5)
.build();
searchResp = client.search(searchReq);
System.out.println(JSONObject.toJSON(searchResp));
// 8. Filtered search
// 8.1 Filter with "like" operator and prefix wildcard
query_vector = [0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592]
res = await client.search({
collection_name: "quick_setup",
data: [query_vector],
limit: 5,
filters: "color_tag like \"red%\"",
output_fields: ["color_tag"]
})
console.log(res.results)
输出结果类似于以下内容:
[
[
{
"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"
}
}
]
]
{"searchResults": [
[
{
"score": 1.1869997,
"fields": {"color_tag": "red_3026"}
},
{
"score": 1.1677284,
"fields": {"color_tag": "red_9030"}
},
{
"score": 1.1476475,
"fields": {"color_tag": "red_3744"}
},
{
"score": 1.0969629,
"fields": {"color_tag": "red_4168"}
},
{
"score": 1.0741848,
"fields": {"color_tag": "red_9678"}
}
]
]}
[
{ score: 2.5080761909484863, id: '1201', color_tag: 'red_8904' },
{ score: 2.491129159927368, id: '425', color_tag: 'purple_8212' },
{ score: 2.4889798164367676, id: '1458', color_tag: 'red_6891' },
{ score: 2.42964243888855, id: '724', color_tag: 'black_9885' },
{ score: 2.4004223346710205, id: '854', color_tag: 'black_5990' }
]
过滤颜色包含字母ll的结果:
# Infix match on color field
res = client.search(
collection_name="test_collection", # 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)
// 8. Filtered search
query_vectors = Arrays.asList(Arrays.asList(0.3580376395471989f, -0.6023495712049978f, 0.18414012509913835f, -0.26286205330961354f, 0.9029438446296592f));
searchReq = SearchReq.builder()
.collectionName("quick_setup")
.data(query_vectors)
.outputFields(Arrays.asList("color_tag"))
.filter("color like \"%ll%\"")
.topK(5)
.build();
searchResp = client.search(searchReq);
System.out.println(JSONObject.toJSON(searchResp));
// 8. Filtered search
// 8.1 Filter with "like" operator and prefix wildcard
query_vector = [0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592]
res = await client.search({
collection_name: "quick_setup",
data: [query_vector],
limit: 5,
filters: "color_tag like \"%ll%\"",
output_fields: ["color_tag"]
})
console.log(res.results)
输出结果类似于以下内容:
[
[
{
"id": 5,
"distance": 0.7972343564033508,
"entity": {
"color": "yellow_4222"
}
}
]
]
{"searchResults": [
[
{
"score": 1.1869997,
"fields": {"color_tag": "yellow_4222"}
}
]
]}
[
{ score: 2.5080761909484863, id: '1201', color_tag: '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="test_collection", # 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)
// 9. Range search
query_vectors = Arrays.asList(Arrays.asList(0.3580376395471989f, -0.6023495712049978f, 0.18414012509913835f, -0.26286205330961354f, 0.9029438446296592f));
searchReq = SearchReq.builder()
.collectionName("quick_setup")
.data(query_vectors)
.outputFields(Arrays.asList("color_tag"))
.searchParams(Map.of("radius", 0.1, "range", 1.0))
.topK(5)
.build();
searchResp = client.search(searchReq);
System.out.println(JSONObject.toJSON(searchResp));
// 9. Range search
query_vector = [0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592]
res = await client.search({
collection_name: "quick_setup",
data: [query_vector],
limit: 5,
params: {
radius: 0.1,
range: 1.0
},
output_fields: ["color_tag"]
})
console.log(res.results)
输出结果类似于以下内容:
[
[
{
"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"
}
}
]
]
{"searchResults": [
[
{
"score": 1.263043,
"fields": {"color_tag": "green_2052"}
},
{
"score": 1.2377806,
"fields": {"color_tag": "purple_3709"}
},
{
"score": 1.1869997,
"fields": {"color_tag": "red_3026"}
},
{
"score": 1.1748955,
"fields": {"color_tag": "black_1646"}
},
{
"score": 1.1720343,
"fields": {"color_tag": "green_4853"}
}
]
]}
[
{ score: 2.3387961387634277, id: '718', color_tag: 'black_7154' },
{ score: 2.3352415561676025, id: '1745', color_tag: 'blue_8741' },
{ score: 2.290485382080078, id: '1408', color_tag: 'red_2324' },
{ score: 2.285870313644409, id: '854', color_tag: 'black_5990' },
{ score: 2.2593345642089844, id: '1309', color_tag: 'red_8458' }
]
您会发现,所有返回的实体与查询向量的距离都在 0.8 到 1.0 之间。
radius
和range_filter
的参数设置因使用的度量类型而异。
度量类型 | 特征 | 搜索范围设置 |
---|---|---|
L2 | L2 距离越小表示相似度越高。 | 要从结果中排除最接近的向量,请确保:range_filter <= distance <radius |
IP | IP 距离越大,表示相似度越高。 | 要从结果中排除最接近的向量,请确保:radius < distance <=range_filter |
COSINE | 余弦值越大,表示相似度越高。 | 要从结果中排除最接近的向量,请确保:radius < distance <=range_filter |
JACCARD | Jaccard 距离越小,表示相似度越高。 | 要从结果中排除最接近的向量,请确保:range_filter <= 距离 <radius |
HAMMING | 汉明距离越小,相似度越高。 | 要从结果中排除最接近的向量,请确保:range_filter <= distance <radius |
要了解有关距离度量类型的更多信息,请参阅相似度量。
分组搜索
在 Milvus 中,按特定字段分组搜索可以避免结果中出现相同字段项的冗余。你可以获得特定字段的不同结果集。
考虑一个文档 Collections,每个文档分成不同的段落。每个段落由一个向量 Embeddings 表示,属于一个文档。要查找相关文档而不是相似段落,可以在search()
命令中加入group_by_field
参数,按文档 ID 对结果进行分组。这有助于返回最相关和最独特的文档,而不是同一文档中的不同段落。
以下是按字段分组搜索结果的示例代码:
# 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=10, # 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]]
print(doc_ids)
输出结果类似于下面的内容:
[5, 10, 1, 7, 9, 6, 3, 4, 8, 2]
在给定的输出中,可以看到返回的实体不包含任何重复的doc_id
值。
为了进行比较,让我们注释掉group_by_field
并进行常规搜索:
# 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=10, # 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]]
print(doc_ids)
输出结果类似于下面的内容:
[1, 10, 3, 10, 1, 9, 4, 4, 8, 6]
在给定的输出中,可以看到返回的实体包含重复的doc_id
值。
限制
索引:此分组功能仅适用于使用HNSW、IVF_ FLAT或FLAT类型编制索引的 Collections。有关详细信息,请参阅内存索引。
向量:目前,分组搜索不支持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 | 如何测量向量 Embeddings 之间的相似性。 可能的值为 IP ,L2 ,COSINE ,JACCARD , 和HAMMING ,默认值为已加载索引文件的值。 |
params.nprobe | 搜索时要查询的单位数量。 取值范围为 [1,nlist[1]]。 |
params.level | 搜索精度级别。 可能的值为 1 、2 和3 ,默认值为1 。值越高,结果越精确,但性能越差。 |
params.radius | 定义搜索空间的外部边界。只有与查询向量距离在此范围内的向量才会被视为潜在匹配。 值范围由 metric_type 参数决定。例如,如果metric_type 设置为L2 ,则有效值范围为[0, ∞] 。如果metric_type 设置为COSINE ,则有效值范围为[-1, 1] 。更多信息,请参阅 "相似度指标"。 |
params.range_filter | radius 设置搜索的外部界限,而range_filter 可选择用于定义内部界限,创建一个距离范围,向量必须在该范围内才会被视为匹配。值范围由 metric_type 参数决定。例如,如果metric_type 设置为L2 ,则有效值范围为[0, ∞] 。如果metric_type 设置为COSINE ,则有效值范围为[-1, 1] 。更多信息,请参阅 "相似度指标"。 |
注释
[1] 索引后的群集单位数。索引 Collections 时,Milvus 会将向量数据细分为多个簇单元,其数量随实际索引设置而变化。
[2] 搜索中返回的实体数量。