单一向量搜索
在插入数据后,下一步是在 Milvus 集合中执行相似性搜索。
Milvus 允许您进行两种类型的搜索,取决于您的集合中向量字段的数量:
- 单一向量搜索:如果您的集合只有一个向量字段,请使用
search()
方法查找最相似的实体。该方法将您的查询向量与集合中现有向量进行比较,并返回最接近的匹配项的 ID,以及它们之间的距离。可选地,它还可以返回结果的向量值和元数据。 - 多向量搜索:对于具有两个或更多向量字段的集合,请使用
hybrid_search()
方法。该方法执行多个近似最近邻(ANN)搜索请求,并组合结果以在重新排序后返回最相关的匹配项。
本指南重点介绍如何在 Milvus 中执行单一向量搜索。有关多向量搜索的详细信息,请参阅 混合搜索。
概述
有各种搜索类型可满足不同需求:
-
基本搜索:包括单一向量搜索、批量向量搜索、分区搜索以及带有指定输出字段的搜索。
-
过滤搜索:根据标量字段应用过滤条件以细化搜索结果。
-
范围搜索:查找与查询向量在特定距离范围内的向量。
-
分组搜索:根据特定字段对搜索结果进行分组,以确保结果的多样性。
准备工作
下面的代码片段重新利用现有代码,建立与 Milvus 的连接并快速设置一个集合。
# 1. 设置 Milvus 客户端
client = MilvusClient(
uri=CLUSTER_ENDPOINT,
token=TOKEN
)
# 2. 创建集合
client.create_collection(
collection_name="quick_setup",
dimension=5,
metric_type="IP"
)
# 3. 插入随机生成的向量
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)
# 输出
#
# {
# "insert_count": 1000,
# "ids": [
# 0,
# 1,
# 2,
# 3,
# 4,
# 5,
# 6,
# 7,
# 8,
# 9,
# "(990 more items hidden)"
# ]
# }
# 6.1 创建分区
client.create_partition(
collection_name="quick_setup",
partition_name="red"
)
client.create_partition(
collection_name="quick_setup",
partition_name="blue"
)
# 6.1 将数据插入分区
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)
# 输出
#
# {
# "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)
# 输出
#
# {
# "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. 连接到 Milvus 服务器
ConnectConfig connectConfig = ConnectConfig.builder()
.uri(CLUSTER_ENDPOINT)
.build();
MilvusClientV2 client = new MilvusClientV2(connectConfig);
// 2. 在快速设置模式下创建一个集合
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);
// 输出:
// true
// 3. 将随机生成的向量插入集合
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));
// 输出:
// {"insertCnt": 1000}
// 6.1. 创建一个分区
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 将数据插入分区
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));
// 输出:
// {"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));
// 输出:
// {"insertCnt": 500}
const { MilvusClient, DataType, sleep } = require("@zilliz/milvus2-sdk-node")
const address = "http://localhost:19530"
// 1. 设置 Milvus 客户端
client = new MilvusClient({address});
// 2. 在快速设置模式下创建一个集合
await client.createCollection({
collection_name: "quick_setup",
dimension: 5,
metric_type: "IP"
});
// 3. 插入随机生成的向量
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)
// 输出
//
// 1000
//
await client.createPartition({
collection_name: "quick_setup",
partition_name: "red"
})
await client.createPartition({
collection_name: "quick_setup",
partition_name: "blue"
})
// 6.1 将数据插入分区
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)
// 输出
//
// 500
//
res = await client.insert({
collection_name: "quick_setup",
data: blue_data,
partition_name: "blue"
})
console.log(res.insert_cnt)
// 输出
//
// 500
//
基本搜索
在发送 search
请求时,您可以提供一个或多个向量值,表示您的查询嵌入,以及一个 limit
值,指示要返回的结果数量。
根据您的数据和查询向量,可能会获得少于 limit
个结果。当 limit
大于查询的可能匹配向量数量时,就会发生这种情况。
单向量搜索
单向量搜索是 Milvus 中最简单的 search
操作形式,旨在找到与给定查询向量最相似的向量。
要执行单向量搜索,请指定目标集合名称、查询向量和所需的结果数量(limit
)。此操作返回一个结果集,包括最相似的向量、它们的 ID 和与查询向量的距离。
以下是搜索与查询向量最相似的前5个实体的示例:
# 单向量搜索
res = client.search(
collection_name="test_collection", # 替换为您的集合实际名称
# 替换为您的查询向量
data=[[0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592]],
limit=5, # 返回的最大搜索结果数
search_params={"metric_type": "IP", "params": {}} # 搜索参数
)
# 将输出转换为格式化的 JSON 字符串
result = json.dumps(res, indent=4)
print(result)
// 4. 单向量搜索
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) // 要返回的结果数
.build();
SearchResp searchResp = client.search(searchReq);
System.out.println(JSONObject.toJSON(searchResp));
// 4. 单向量搜索
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, // 要返回的结果数
})
console.log(res.results)
输出类似于以下内容:
[
[
{
"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
字段中包含多个查询向量。系统会并行处理这些向量,为每个查询向量返回一个单独的结果集,每个集合包含在集合中找到的最接近的匹配项。
以下是搜索两组不同的最相似实体的示例查询向量:
# 批量向量搜索
res = client.search(
collection_name="test_collection", # 替换为您的集合实际名称
data=[
[0.19886812562848388, 0.06023560599112088, 0.6976963061752597, 0.2614474506242501, 0.838729485096104],
[0.3172005263489739, 0.9719044792798428, -0.36981146090600725, -0.4860894583077995, 0.95791889146345]
], # 替换为您的查询向量
limit=2, # 返回的最大搜索结果数
search_params={"metric_type": "IP", "params": {}} # 搜索参数
)
result = json.dumps(res, indent=4)
print(result)
// 5. 批量向量搜索
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. 批量向量搜索
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)
输出类似于以下内容:
```python
[
[
{
"id": 1,
"distance": 1.3017789125442505,
"entity": {}
},
{
"id": 7,
"distance": 1.2419954538345337,
"entity": {}
}
], # 结果集 1
[
{
"id": 3,
"distance": 2.3358664512634277,
"entity": {}
},
{
"id": 8,
"distance": 0.5642921924591064,
"entity": {}
}
] # 结果集 2
]
// 两组向量的返回结果如预期所示
{"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
}
}
]
]}