Itérateur de recherche
La recherche ANN impose une limite maximale au nombre d'entités pouvant être récupérées lors d'une seule requête, et l'utilisation de la recherche ANN de base peut ne pas suffire pour répondre aux besoins d'une recherche à grande échelle. Pour les requêtes de recherche ANN où la valeur de topK dépasse 16 384, il est conseillé d'envisager l'utilisation du SearchIterator. Cette section présente l'utilisation du SearchIterator et les considérations associées.
Présentation
Une requête de recherche renvoie des résultats de recherche, tandis qu’un SearchIterator renvoie un itérateur. Vous pouvez appeler la méthode next() de cet itérateur pour obtenir les résultats de recherche.
Plus précisément, vous pouvez utiliser les SearchIterators comme suit :
Créez un SearchIterator et définissez le nombre d’entités à renvoyer par requête de recherche ainsi que le nombre total d’entités à renvoyer.
Appelez la méthode next() du SearchIterator dans une boucle pour obtenir les résultats de recherche de manière paginée.
Appelez la méthode close() de l'itérateur pour mettre fin à la boucle si la méthode next() renvoie un résultat vide.
Créer un SearchIterator
L'extrait de code suivant montre comment créer un SearchIterator.
from pymilvus import connections, Collection
connections.connect(
uri="http://localhost:19530",
token="root:Milvus"
)
# create iterator
query_vectors = [
[0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592]]
collection = Collection("iterator_collection")
iterator = collection.search_iterator(
data=query_vectors,
anns_field="vector",
param={"metric_type": "L2", "params": {"nprobe": 16}},
batch_size=50,
output_fields=["color"],
limit=20000
)
import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.orm.iterator.SearchIterator;
import io.milvus.v2.common.IndexParam.MetricType;
import io.milvus.v2.service.vector.request.data.FloatVec;
import java.util.*;
MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder()
.uri("http://localhost:19530")
.token("root:Milvus")
.build());
FloatVec queryVector = new FloatVec(new float[]{0.3580376395471989f, -0.6023495712049978f, 0.18414012509913835f, -0.26286205330961354f, 0.9029438446296592f});
SearchIterator searchIterator = client.searchIterator(SearchIteratorReq.builder()
.collectionName("iterator_collection")
.vectors(Collections.singletonList(queryVector))
.vectorFieldName("vector")
.batchSize(500L)
.outputFields(Lists.newArrayList("color"))
.topK(20000)
.metricType(IndexParam.MetricType.COSINE)
.build());
// go
import { MilvusClient } from '@zilliz/milvus2-sdk-node';
const milvusClient = new MilvusClient({
address: 'http://localhost:19530',
token: 'root:Milvus',
});
const queryVectors = [
[0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592],
];
const collectionName = 'iterator_collection';
const iterator = milvusClient.searchIterator({
collection_name: collectionName,
vectors: queryVectors,
anns_field: 'vector',
params: { metric_type: 'L2', params: { nprobe: 16 } },
batch_size: 50,
output_fields: ['color'],
limit: 20000,
});
export CLUSTER_ENDPOINT="http://localhost:19530"
export TOKEN="root:Milvus"
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/search" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
-d '{
"collectionName": "iterator_collection",
"annsField": "vector",
"data": [[0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592]],
"searchParams": {
"metricType": "L2",
"params": {
"nprobe": 16
}
},
"limit": 50,
"offset": 0,
"outputFields": ["color"]
}'
#include <iostream>
#include <vector>
#include "milvus/MilvusClientV2.h"
auto client = milvus::MilvusClientV2::Create();
auto status = client->Connect(milvus::ConnectParam("http://localhost:19530", "root:Milvus"));
if (!status.IsOk()) {
std::cerr << "Failed to connect: " << status.Message() << std::endl;
return;
}
// create iterator
std::vector<float> queryVector = {
0.35803764F, -0.60234958F, 0.18414013F, -0.26286206F, 0.90294385F
};
milvus::SearchIteratorRequest request;
request.SetCollectionName("iterator_collection");
request.SetAnnsField("vector");
request.SetMetricType(milvus::MetricType::L2);
request.AddExtraParam("nprobe", "16");
request.SetBatchSize(50);
request.AddOutputField("color");
request.SetLimit(20000);
// SearchIterator only accepts one vector
request.AddFloatVector(queryVector);
milvus::SearchIteratorPtr iterator;
status = client->SearchIterator(request, iterator);
if (!status.IsOk()) {
std::cerr << "Failed to create search iterator: " << status.Message() << std::endl;
return;
}
Dans les exemples ci-dessus, vous avez défini le nombre d’entités à renvoyer par recherche (batch_size/batchSize) à 50, et le nombre total d’entités à renvoyer (topK) à 20 000.
Utilisation de SearchIterator
Une fois le SearchIterator prêt, vous pouvez appeler sa méthode next() pour obtenir les résultats de la recherche de manière paginée.
results = []
while True:
result = iterator.next()
if not result:
iterator.close()
break
for hit in result:
results.append(hit.to_dict())
import io.milvus.response.QueryResultsWrapper;
while (true) {
List<QueryResultsWrapper.RowRecord> res = searchIterator.next();
if (res.isEmpty()) {
searchIterator.close();
break;
}
for (QueryResultsWrapper.RowRecord record : res) {
System.out.println(record);
}
}
// go
for await (const result of iterator) {
console.log(result);
}
export CLUSTER_ENDPOINT="http://localhost:19530"
export TOKEN="root:Milvus"
batch_size=50
limit=20000
offset=0
# Paginate with offset until an empty page is returned. Note that the sum of
# offset and limit in each request must not exceed the server-side result
# window (16,384 by default); SDK search iterators do not have this limit.
while [ "$offset" -lt "$limit" ]; do
response=$(curl --silent --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/search" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
-d '{
"collectionName": "iterator_collection",
"annsField": "vector",
"data": [[0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592]],
"searchParams": {
"metricType": "L2",
"params": {
"nprobe": 16
}
},
"limit": '"$batch_size"',
"offset": '"$offset"',
"outputFields": ["color"]
}')
count=$(echo "$response" | jq -r '.data | length')
if [ "$count" -eq 0 ]; then
break
fi
echo "$response" | jq -r '.data[]'
offset=$((offset + batch_size))
done
while (true) {
milvus::SingleResult result;
status = iterator->Next(result);
if (!status.IsOk()) {
std::cerr << "Iterator next failed: " << status.Message() << std::endl;
break;
}
if (result.GetRowCount() == 0) {
break;
}
milvus::EntityRows rows;
status = result.OutputRows(rows);
if (!status.IsOk()) {
std::cerr << "Failed to get output rows: " << status.Message() << std::endl;
break;
}
for (const auto& row : rows) {
std::cout << row.dump() << std::endl;
}
}
Dans les exemples de code ci-dessus, vous avez créé une boucle infinie et appelé la méthode next() dans la boucle pour stocker les résultats de la recherche dans une variable, puis fermé l’itérateur lorsque la méthode next() ne renvoie plus rien.