Ricerca per chiave primariaCompatible with Milvus 2.6.9+
Quando si eseguono ricerche per similarità, viene sempre richiesto di fornire uno o più vettori di query, anche se questi sono già presenti nella raccolta di destinazione. Per evitare di recuperare i vettori prima della ricerca, è possibile utilizzare invece le chiavi primarie.
Panoramica
Sulle piattaforme di e-commerce, gli utenti possono inserire una parola chiave per recuperare i prodotti corrispondenti. Una volta che l'utente visualizza la pagina dei dettagli di un prodotto, la piattaforma mostra anche un elenco di prodotti simili nella parte inferiore della pagina per gli utenti che desiderano confrontarli.
I suggerimenti sono ordinati in base alla loro somiglianza con la parola chiave o con il prodotto corrente. Per ottenere questo risultato, gli sviluppatori della piattaforma devono recuperare da Milvus la rappresentazione vettoriale della parola chiave o del prodotto corrente prima della ricerca di somiglianza vera e propria; ciò aumenta il numero di scambi tra la piattaforma e Milvus e comporta la trasmissione attraverso la rete di un gran numero di valori in virgola mobile ad alta dimensionalità.
Per semplificare la logica di interazione tra le vostre applicazioni e Milvus, ridurre il numero di scambi e evitare la trasmissione di grandi quantità di valori in virgola mobile ad alta dimensionalità attraverso la rete, prendete in considerazione l’utilizzo delle ricerche per chiave primaria.
In una ricerca per chiave primaria, non è necessario fornire alcun vettore di query. Vi verrà invece richiesto di fornire le chiavi primarie (ids) delle entità che contengono i vettori di query.
Limiti e restrizioni
Le ricerche che utilizzano le chiavi primarie si applicano a tutti i tipi di dati vettoriali, ad eccezione dei campi vettoriali sparsi derivati da campi VarChar, come nelle funzioni BM25.
È possibile utilizzare le chiavi primarie al posto dei vettori di query nelle ricerche filtrate, per intervallo e di raggruppamento, con l'opzione di abilitare l'impaginazione. Tuttavia, questa funzionalità non si applica alle ricerche ibride e agli iteratori di ricerca.
Per le ricerche di similarità che coinvolgono elenchi di embedding, è comunque necessario recuperare i vettori di query, organizzarli in elenchi di embedding ed eseguire le ricerche.
Non è possibile utilizzare le chiavi primarie al posto dei vettori di query nelle API RESTful.
Per eventuali chiavi primarie inesistenti o in formato errato, Milvus segnalerà degli errori.
Le chiavi primarie e i vettori di query si escludono a vicenda. Fornire entrambi comporta anch’esso la generazione di errori.
Esempi
Gli esempi seguenti presuppongono che tutti gli ID Int64 forniti siano disponibili nella collezione di destinazione.
Le chiavi primarie non vengono utilizzate per il filtraggio, ma solo per il recupero dei vettori.
Esempio 1: Ricerca di base con chiave primaria
Per eseguire una ricerca di base con chiave primaria, è sufficiente sostituire i vettori di query con le chiavi primarie.
from pymilvus import MilvusClient
client = MilvusClient(
uri="http://localhost:19530",
token="root:Milvus"
)
res = client.search(
collection_name="quick_setup",
anns_field="vector",
ids=[551, 296, 43], # a list of primary keys
limit=3,
search_params={"metric_type": "IP"}
)
for hits in res:
for hit in hits:
print(hit)
import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.service.vector.request.SearchReq;
import io.milvus.v2.service.vector.response.SearchResp;
import io.milvus.v2.common.IndexParam;
MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder()
.uri("http://localhost:19530")
.token("root:Milvus")
.build());
List<Object> ids = Arrays.asList(551L, 296L, 43L);
SearchResp searchResp = client.search(SearchReq.builder()
.collectionName("quick_setup")
.annsField("vector")
.ids(ids)
.limit(3)
.metricType(IndexParam.MetricType.IP)
.build());
List<List<SearchResp.SearchResult>> searchResults = searchResp.getSearchResults();
for (List<SearchResp.SearchResult> results : searchResults) {
System.out.println("TopK results:");
for (SearchResp.SearchResult result : results) {
System.out.println(result);
}
}
import { MilvusClient } from "@zilliz/milvus2-sdk-node";
const client = new MilvusClient({
address: "http://localhost:19530",
token: "root:Milvus",
});
const res = await client.search({
collection_name: "quick_setup",
anns_field: "vector",
ids: [551, 296, 43], // a list of primary keys
limit: 3,
metric_type: "IP",
});
console.log(res.results);
import (
"context"
"fmt"
"github.com/milvus-io/milvus/client/v3/column"
"github.com/milvus-io/milvus/client/v3/milvusclient"
)
ctx := context.Background()
client, err := milvusclient.New(ctx, &milvusclient.ClientConfig{
Address: "localhost:19530",
APIKey: "root:Milvus",
})
if err != nil {
fmt.Println(err.Error())
// handle error
}
defer client.Close(ctx)
ids := column.NewColumnInt64("id", []int64{551, 296, 43}) // a list of primary keys
resultSets, err := client.Search(ctx, milvusclient.NewSearchByIDsOption(
"quick_setup", // collectionName
3, // limit
ids,
).WithANNSField("vector").
WithSearchParam("metric_type", "IP"))
if err != nil {
fmt.Println(err.Error())
// handle error
}
for _, resultSet := range resultSets {
fmt.Println("IDs: ", resultSet.IDs)
fmt.Println("Scores: ", resultSet.Scores)
}
# restful
curl -X POST "http://localhost:19530/v2/vectordb/entities/search" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer root:Milvus" \
-H "Request-Timeout: 10" \
-d '{
"collectionName": "quick_setup",
"annsField": "vector",
"ids": [551, 296, 43],
"limit": 3
}'
auto searchRequest = milvus::SearchRequest()
.WithCollectionName("quick_setup")
.WithAnnsField("vector")
.WithIDs({551, 296, 43})
.WithLimit(3)
.WithMetricType(milvus::MetricType::IP);
milvus::SearchResponse searchResponse;
auto status = client->Search(searchRequest, searchResponse);
if (!status.IsOk()) {
std::cerr << "Search failed: " << status.Message() << std::endl;
return;
}
for (const auto& result : searchResponse.Results().Results()) {
const auto ids = result.Ids().IntIDArray();
for (size_t i = 0; i < result.Scores().size(); ++i) {
std::cout << "id=" << ids[i] << ", score=" << result.Scores()[i] << std::endl;
}
}
Esempio 2: Ricerca filtrata tramite chiavi primarie
L’esempio seguente presuppone che «color» e «likes» siano due campi definiti dallo schema nella collezione di destinazione.
res = client.search(
collection_name="my_collection",
ids=[551, 296, 43], #
filter='color like "red%" and likes > 50',
output_fields=["color", "likes"],
limit=3,
)
List<Object> ids = Arrays.asList(551L, 296L, 43L);
SearchResp searchResp = client.search(SearchReq.builder()
.collectionName("my_collection")
.ids(ids)
.filter("color like \"red%\" and likes > 50")
.limit(3)
.outputFields(Arrays.asList("color", "likes"))
.build());
List<List<SearchResp.SearchResult>> searchResults = searchResp.getSearchResults();
for (List<SearchResp.SearchResult> results : searchResults) {
System.out.println("TopK results:");
for (SearchResp.SearchResult result : results) {
System.out.println(result);
}
}
const res = await client.search({
collection_name: "my_collection",
ids: [551, 296, 43],
filter: 'color like "red%" and likes > 50',
output_fields: ["id", "color", "likes"],
limit: 3,
});
console.log(res.results);
ids := column.NewColumnInt64("id", []int64{551, 296, 43})
resultSets, err := client.Search(ctx, milvusclient.NewSearchByIDsOption(
"my_collection", // collectionName
3, // limit
ids,
).WithFilter(`color like "red%" and likes > 50`).
WithOutputFields("color", "likes"))
if err != nil {
fmt.Println(err.Error())
// handle error
}
for _, resultSet := range resultSets {
fmt.Println("IDs: ", resultSet.IDs)
fmt.Println("Scores: ", resultSet.Scores)
fmt.Println("color: ", resultSet.GetColumn("color"))
fmt.Println("likes: ", resultSet.GetColumn("likes"))
}
# restful
curl -X POST "http://localhost:19530/v2/vectordb/entities/search" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer root:Milvus" \
-H "Request-Timeout: 10" \
-d '{
"collectionName": "my_collection",
"annsField": "vector",
"ids": [551, 296, 43],
"filter": "color like \\"red%\\" and likes > 50",
"outputFields": ["color", "likes"],
"limit": 3
}'
auto searchRequest = milvus::SearchRequest()
.WithCollectionName("my_collection")
.WithIDs({551, 296, 43})
.WithFilter(R"(color like "red%" and likes > 50)")
.WithOutputFields({"color", "likes"})
.WithLimit(3);
milvus::SearchResponse searchResponse;
auto status = client->Search(searchRequest, searchResponse);
if (!status.IsOk()) {
std::cerr << "Search failed: " << status.Message() << std::endl;
return;
}
for (const auto& result : searchResponse.Results().Results()) {
const auto ids = result.Ids().IntIDArray();
const auto colors = result.OutputField<milvus::VarCharFieldData>("color");
const auto likes = result.OutputField<milvus::Int64FieldData>("likes");
for (size_t i = 0; i < result.Scores().size(); ++i) {
std::cout << "id=" << ids[i]
<< ", score=" << result.Scores()[i]
<< ", color=" << colors->Data()[i]
<< ", likes=" << likes->Data()[i] << std::endl;
}
}
Esempio 3: Ricerca per intervallo utilizzando le chiavi primarie
res = client.search(
collection_name="my_collection",
ids=[551, 296, 43],
limit=3,
search_params={
"params": {
"radius": 0.4,
"range_filter": 0.6
}
}
)
Map<String, Object> params = new HashMap<>();
params.put("radius", "0.4");
params.put("range_filter", "0.6");
List<Object> ids = Arrays.asList(551L, 296L, 43L);
SearchResp searchResp = client.search(SearchReq.builder()
.collectionName("my_collection")
.ids(ids)
.limit(3)
.searchParams(params)
.build());
List<List<SearchResp.SearchResult>> searchResults = searchResp.getSearchResults();
for (List<SearchResp.SearchResult> results : searchResults) {
System.out.println("TopK results:");
for (SearchResp.SearchResult result : results) {
System.out.println(result);
}
}
const res = await client.search({
collection_name: "my_collection",
ids: [551, 296, 43],
limit: 3,
params: {
radius: 0.4,
range_filter: 0.6,
},
});
console.log(res.results);
annParam := index.NewCustomAnnParam()
annParam.WithRadius(0.4)
annParam.WithRangeFilter(0.6)
ids := column.NewColumnInt64("id", []int64{551, 296, 43})
resultSets, err := client.Search(ctx, milvusclient.NewSearchByIDsOption(
"my_collection", // collectionName
3, // limit
ids,
).WithANNSField("vector").
WithAnnParam(annParam))
if err != nil {
fmt.Println(err.Error())
// handle error
}
for _, resultSet := range resultSets {
fmt.Println("IDs: ", resultSet.IDs)
fmt.Println("Scores: ", resultSet.Scores)
}
# restful
curl -X POST "http://localhost:19530/v2/vectordb/entities/search" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer root:Milvus" \
-H "Request-Timeout: 10" \
-d '{
"collectionName": "my_collection",
"annsField": "vector",
"ids": [551, 296, 43],
"limit": 3,
"searchParams": {
"params": {
"radius": 0.4,
"range_filter": 0.6
}
}
}'
auto searchRequest = milvus::SearchRequest()
.WithCollectionName("my_collection")
.WithAnnsField("vector")
.WithIDs({551, 296, 43})
.WithRadius(0.4)
.WithRangeFilter(0.6)
.WithLimit(3);
milvus::SearchResponse searchResponse;
auto status = client->Search(searchRequest, searchResponse);
if (!status.IsOk()) {
std::cerr << "Search failed: " << status.Message() << std::endl;
return;
}
for (const auto& result : searchResponse.Results().Results()) {
const auto ids = result.Ids().IntIDArray();
for (size_t i = 0; i < result.Scores().size(); ++i) {
std::cout << "id=" << ids[i] << ", score=" << result.Scores()[i] << std::endl;
}
}
Esempio 4: Ricerca per raggruppamento utilizzando le chiavi primarie
L'esempio seguente presuppone ch docId i siano campi definiti dallo schema nella collezione di destinazione.
res = client.search(
collection_name="my_collection",
ids=[551, 296, 43],
limit=3,
group_by_field="docId",
output_fields=["docId"]
)
List<Object> ids = Arrays.asList(551L, 296L, 43L);
SearchResp searchResp = client.search(SearchReq.builder()
.collectionName("my_collection")
.ids(ids)
.limit(3)
.groupByFieldName("docId")
.outputFields(Collections.singletonList("docId"))
.build());
List<List<SearchResp.SearchResult>> searchResults = searchResp.getSearchResults();
for (List<SearchResp.SearchResult> results : searchResults) {
System.out.println("TopK results:");
for (SearchResp.SearchResult result : results) {
System.out.println(result);
}
}
const res = await client.search({
collection_name: "my_collection",
ids: [551, 296, 43],
limit: 3,
group_by_field: "docId",
output_fields: ["id", "docId"],
});
console.log(res.results);
ids := column.NewColumnInt64("id", []int64{551, 296, 43})
resultSets, err := client.Search(ctx, milvusclient.NewSearchByIDsOption(
"my_collection", // collectionName
3, // limit
ids,
).WithGroupByField("docId").
WithOutputFields("docId"))
if err != nil {
fmt.Println(err.Error())
// handle error
}
for _, resultSet := range resultSets {
fmt.Println("IDs: ", resultSet.IDs)
fmt.Println("Scores: ", resultSet.Scores)
fmt.Println("docId: ", resultSet.GetColumn("docId"))
}
# restful
curl -X POST "http://localhost:19530/v2/vectordb/entities/search" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer root:Milvus" \
-H "Request-Timeout: 10" \
-d '{
"collectionName": "my_collection",
"annsField": "vector",
"ids": [551, 296, 43],
"limit": 3,
"groupingField": "docId",
"outputFields": ["docId"]
}'
auto searchRequest = milvus::SearchRequest()
.WithCollectionName("my_collection")
.WithAnnsField("vector")
.WithIDs({551, 296, 43})
.WithGroupByField("docId")
.WithOutputFields({"docId"})
.WithLimit(3);
milvus::SearchResponse searchResponse;
auto status = client->Search(searchRequest, searchResponse);
if (!status.IsOk()) {
std::cerr << "Search failed: " << status.Message() << std::endl;
return;
}
for (const auto& result : searchResponse.Results().Results()) {
const auto ids = result.Ids().IntIDArray();
const auto docIds = result.OutputField<milvus::Int64FieldData>("docId");
for (size_t i = 0; i < result.Scores().size(); ++i) {
std::cout << "id=" << ids[i]
<< ", score=" << result.Scores()[i]
<< ", docId=" << docIds->Data()[i] << std::endl;
}
}