Iterador de pesquisa
A Pesquisa ANN tem um limite máximo para o número de entidades que podem ser recuperadas numa única consulta, e a simples utilização da Pesquisa ANN básica pode não satisfazer as exigências de uma recuperação em grande escala. Para pedidos de pesquisa ANN em que o topK exceda 16 384, é aconselhável considerar a utilização do SearchIterator. Esta secção irá apresentar como utilizar o SearchIterator e as considerações relacionadas.
Visão geral
Uma solicitação de pesquisa devolve resultados de pesquisa, enquanto um SearchIterator devolve um iterador. Pode chamar o método next() deste iterador para obter os resultados da pesquisa.
Mais especificamente, pode utilizar o SearchIterator da seguinte forma:
Crie um SearchIterator e defina o número de entidades a devolver por pedido de pesquisa e o número total de entidades a devolver.
Chame o método next() do SearchIterator num ciclo para obter os resultados da pesquisa de forma paginada.
Chame o método close() do iterador para terminar o ciclo se o método next() devolver um resultado vazio.
Criar um SearchIterator
O trecho de código seguinte demonstra como criar um 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;
}
Nos exemplos acima, definiu o número de entidades a devolver por pesquisa (batch_size/batchSize) para 50 e o número total de entidades a devolver (topK) para 20 000.
Utilizar o SearchIterator
Assim que o SearchIterator estiver pronto, pode chamar o seu método next() para obter os resultados da pesquisa de forma paginada.
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;
}
}
Nos exemplos de código acima, criou-se um ciclo infinito e chamou-se o método next() dentro do ciclo para armazenar os resultados da pesquisa numa variável, tendo-se encerrado o iterador quando o método next() não devolveu qualquer resultado.