Recherche hybride
La recherche hybride est une méthode de recherche qui effectue simultanément plusieurs recherches ANN, réorganise plusieurs ensembles de résultats issus de ces recherches ANN et renvoie finalement un seul ensemble de résultats. L'utilisation de la recherche hybride peut améliorer la précision de la recherche. Zilliz permet d'effectuer une recherche hybride sur une collection comportant plusieurs champs vectoriels.
La recherche hybride est le plus souvent utilisée dans des scénarios incluant des recherches vectorielles peu denses et des recherches multimodales. Ce guide montre comment effectuer une recherche hybride dans Zilliz à l'aide d'un exemple spécifique.
Scénarios
La recherche hybride convient aux deux scénarios suivants.
Recherche de vecteurs denses et épars
Différents types de vecteurs peuvent représenter différentes informations, et l'utilisation de différents modèles d'intégration peut représenter de manière plus complète différentes caractéristiques et aspects des données. Par exemple, l'utilisation de différents modèles d'intégration pour la même phrase peut générer un vecteur dense pour représenter le sens sémantique et un vecteur clairsemé pour représenter la fréquence des mots dans la phrase.
Vecteurs épars : Les vecteurs épars se caractérisent par leur dimensionnalité élevée et la présence de quelques valeurs non nulles. Cette structure les rend particulièrement adaptés aux applications traditionnelles de recherche d'informations. Dans la plupart des cas, le nombre de dimensions utilisées dans les vecteurs épars correspond à différents tokens dans une ou plusieurs langues. Chaque dimension se voit attribuer une valeur qui indique l'importance relative de ce mot dans le document. Cette disposition s'avère avantageuse pour les tâches qui impliquent la mise en correspondance de textes.
Vecteurs denses : Les vecteurs denses sont des encastrements dérivés des réseaux neuronaux. Lorsqu'ils sont disposés dans un tableau ordonné, ces vecteurs capturent l'essence sémantique du texte d'entrée. Il convient de noter que les vecteurs denses ne sont pas limités au traitement de texte ; ils sont également largement utilisés dans le domaine de la vision par ordinateur pour représenter la sémantique des données visuelles. Ces vecteurs denses, généralement générés par des modèles d'intégration de texte, sont caractérisés par le fait que la plupart ou tous les éléments sont non nuls. Les vecteurs denses sont donc particulièrement efficaces pour les applications de recherche sémantique, car ils peuvent renvoyer les résultats les plus similaires sur la base de la distance vectorielle, même en l'absence de correspondances textuelles exactes. Cette capacité permet d'obtenir des résultats de recherche plus nuancés et tenant compte du contexte, en saisissant souvent des relations entre des concepts qui pourraient échapper aux approches basées sur les mots-clés.
Pour plus de détails, voir Vecteur clair et Vecteur dense.
Recherche multimodale
La recherche multimodale fait référence à la recherche de similarités entre des données non structurées et plusieurs modalités (images, vidéos, audio, texte, etc.). Par exemple, une personne peut être représentée à l'aide de différentes modalités de données telles que les empreintes digitales, les empreintes vocales et les caractéristiques faciales. La recherche hybride permet d'effectuer plusieurs recherches simultanément. Par exemple, la recherche d'une personne avec des empreintes digitales et des empreintes vocales similaires.
Déroulement des opérations
La procédure principale pour effectuer une recherche hybride est la suivante.
Générer des vecteurs denses à l'aide de modèles d'intégration tels que BERT et Transformers.
Générer des vecteurs peu denses à l'aide de modèles d'intégration tels que BM25, BGE-M3, SPLADE, etc.
Créer une collection dans Zilliz et définir le schéma de la collection qui inclut les champs de vecteurs denses et épars.
Insérez les vecteurs peu denses dans la collection créée à l'étape précédente.
Effectuez une recherche hybride : La recherche ANN sur les vecteurs denses renverra un ensemble de K premiers résultats les plus similaires, et la correspondance de texte sur les vecteurs peu denses renverra également un ensemble de K premiers résultats.
Normalisation : Normaliser les scores des deux ensembles de résultats les plus similaires, en convertissant les scores en une plage comprise entre [0 et 1].
Choisir une stratégie de reclassement appropriée pour fusionner et reclasser les deux ensembles de résultats top-K et obtenir un ensemble final de résultats top-K.
Processus de recherche hybride
Exemples
Cette section utilise un exemple spécifique pour illustrer comment effectuer une recherche hybride sur des vecteurs peu denses afin d'améliorer la précision des recherches de texte.
Créer une collection avec plusieurs champs vectoriels
Le processus de création d'une collection comprend trois parties : la définition du schéma de la collection, la configuration des paramètres de l'index et la création de la collection.
Définir le schéma
Dans cet exemple, plusieurs champs vectoriels doivent être définis dans le schéma de la collection. Actuellement, chaque collection peut inclure jusqu'à 4 champs vectoriels par défaut. Mais vous pouvez également modifier la valeur de proxy.maxVectorFieldNum
pour inclure jusqu'à 10 champs vectoriels dans une collection.
L'exemple suivant définit un schéma de collection, où dense
et sparse
sont les deux champs vectoriels.
id
: Ce champ sert de clé primaire pour le stockage des identifiants de texte. Le type de données de ce champ est INT64.text
: Ce champ est utilisé pour stocker le contenu textuel. Le type de données de ce champ est VARCHAR, avec une longueur maximale de 1000 caractères.dense
: Ce champ est utilisé pour stocker les vecteurs denses des textes. Le type de données de ce champ est FLOAT_VECTOR, avec une dimension vectorielle de 768.sparse
: Ce champ est utilisé pour stocker les vecteurs peu denses des textes. Le type de données de ce champ est SPARSE_FLOAT_VECTOR.
# Create a collection in customized setup mode
from pymilvus import (
MilvusClient, DataType
)
client = MilvusClient(
uri="http://localhost:19530",
token="root:Milvus"
)
# Create schema
schema = MilvusClient.create_schema(
auto_id=False,
enable_dynamic_field=True,
)
# Add fields to schema
schema.add_field(field_name="id", datatype=DataType.INT64, is_primary=True)
schema.add_field(field_name="text", datatype=DataType.VARCHAR, max_length=1000)
schema.add_field(field_name="sparse", datatype=DataType.SPARSE_FLOAT_VECTOR)
schema.add_field(field_name="dense", datatype=DataType.FLOAT_VECTOR, dim=5)
import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.common.DataType;
import io.milvus.v2.service.collection.request.AddFieldReq;
import io.milvus.v2.service.collection.request.CreateCollectionReq;
MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder()
.uri("http://localhost:19530")
.token("root:Milvus")
.build());
CreateCollectionReq.CollectionSchema schema = client.createSchema();
schema.addField(AddFieldReq.builder()
.fieldName("id")
.dataType(DataType.Int64)
.isPrimaryKey(true)
.autoID(false)
.build());
schema.addField(AddFieldReq.builder()
.fieldName("text")
.dataType(DataType.VarChar)
.maxLength(1000)
.build());
schema.addField(AddFieldReq.builder()
.fieldName("dense")
.dataType(DataType.FloatVector)
.dimension(768)
.build());
schema.addField(AddFieldReq.builder()
.fieldName("sparse")
.dataType(DataType.SparseFloatVector)
.build());
// WIP
import { MilvusClient, DataType } from "@zilliz/milvus2-sdk-node";
const address = "http://localhost:19530";
const token = "root:Milvus";
const client = new MilvusClient({address, token});
// Create a collection in customized setup mode
// Define fields
const fields = [
{
name: "id",
data_type: DataType.Int64,
is_primary_key: true,
auto_id: false
},
{
name: "text",
data_type: DataType.VarChar,
max_length: 1000
},
{
name: "sparse",
data_type: DataType.SPARSE_FLOAT_VECTOR
},
{
name: "dense",
data_type: DataType.FloatVector,
dim: 768
}
]
export schema='{
"autoId": false,
"enabledDynamicField": true,
"fields": [
{
"fieldName": "id",
"dataType": "Int64",
"isPrimary": true
},
{
"fieldName": "text",
"dataType": "VarChar",
"elementTypeParams": {
"max_length": 1000
}
},
{
"fieldName": "sparse",
"dataType": "SparseFloatVector"
},
{
"fieldName": "dense",
"dataType": "FloatVector",
"elementTypeParams": {
"dim": "768"
}
}
]
}'
Lors des recherches de vecteurs épars, vous pouvez simplifier le processus de génération de vecteurs d'intégration épars en tirant parti des fonctionnalités de la recherche plein texte. Pour plus de détails, voir Recherche en texte intégral.
Créer un index
Après avoir défini le schéma de la collection, il est nécessaire de configurer les index des vecteurs et les métriques de similarité. Dans cet exemple, un index IVF_FLAT est créé pour le champ vectoriel dense dense
et un index SPARSE_INVERTED_INDEX est créé pour le champ vectoriel clairsemé sparse
. Pour en savoir plus sur les types d'index pris en charge, voir Index Explained.
from pymilvus import MilvusClient
# Prepare index parameters
index_params = client.prepare_index_params()
# Add indexes
index_params.add_index(
field_name="dense",
index_name="dense_index",
index_type="IVF_FLAT",
metric_type="IP",
params={"nlist": 128},
)
index_params.add_index(
field_name="sparse",
index_name="sparse_index",
index_type="SPARSE_INVERTED_INDEX", # Index type for sparse vectors
metric_type="IP", # Currently, only IP (Inner Product) is supported for sparse vectors
params={"drop_ratio_build": 0.2}, # The ratio of small vector values to be dropped during indexing
)
import io.milvus.v2.common.IndexParam;
import java.util.*;
Map<String, Object> denseParams = new HashMap<>();
denseParams.put("nlist", 128);
IndexParam indexParamForDenseField = IndexParam.builder()
.fieldName("dense")
.indexName("dense_index")
.indexType(IndexParam.IndexType.IVF_FLAT)
.metricType(IndexParam.MetricType.IP)
.extraParams(denseParams)
.build();
Map<String, Object> sparseParams = new HashMap<>();
sparseParams.put("drop_ratio_build", 0.2);
IndexParam indexParamForSparseField = IndexParam.builder()
.fieldName("sparse")
.indexName("sparse_index")
.indexType(IndexParam.IndexType.SPARSE_INVERTED_INDEX)
.metricType(IndexParam.MetricType.IP)
.extraParams(sparseParams)
.build();
List<IndexParam> indexParams = new ArrayList<>();
indexParams.add(indexParamForDenseField);
indexParams.add(indexParamForSparseField);
const index_params = [{
field_name: "dense",
index_type: "IVF_FLAT",
metric_type: "IP"
},{
field_name: "sparse",
index_type: "SPARSE_INVERTED_INDEX",
metric_type: "IP"
}]
export indexParams='[
{
"fieldName": "dense",
"metricType": "IP",
"indexName": "dense_index",
"indexType":"IVF_FLAT",
"params":{"nlist":128}
},
{
"fieldName": "sparse",
"metricType": "IP",
"indexName": "sparse_index",
"indexType": "SPARSE_INVERTED_INDEX"
}
]'
Création d'une collection
Créez une collection nommée demo
avec le schéma de collection et les index configurés dans les deux étapes précédentes.
from pymilvus import MilvusClient
client.create_collection(
collection_name="hybrid_search_collection",
schema=schema,
index_params=index_params
)
CreateCollectionReq createCollectionReq = CreateCollectionReq.builder()
.collectionName("hybrid_search_collection")
.collectionSchema(schema)
.indexParams(indexParams)
.build();
client.createCollection(createCollectionReq);
res = await client.createCollection({
collection_name: "hybrid_search_collection",
fields: fields,
index_params: index_params,
})
export CLUSTER_ENDPOINT="http://localhost:19530"
export TOKEN="root:Milvus"
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/collections/create" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-d "{
\"collectionName\": \"hybrid_search_collection\",
\"schema\": $schema,
\"indexParams\": $indexParams
}"
Insérer les données
Insérez les vecteurs peu denses dans la collection demo
.
from pymilvus import MilvusClient
data=[
{"id": 0, "text": "Artificial intelligence was founded as an academic discipline in 1956.", "sparse":{9637: 0.30856525997853057, 4399: 0.19771651149001523, ...}, "dense": [0.3580376395471989, -0.6023495712049978, 0.18414012509913835, ...]},
{"id": 1, "text": "Alan Turing was the first person to conduct substantial research in AI.", "sparse":{6959: 0.31025067641541815, 1729: 0.8265339135915016, ...}, "dense": [0.19886812562848388, 0.06023560599112088, 0.6976963061752597, ...]},
{"id": 2, "text": "Born in Maida Vale, London, Turing was raised in southern England.", "sparse":{1220: 0.15303302147479103, 7335: 0.9436728846033107, ...}, "dense": [0.43742130801983836, -0.5597502546264526, 0.6457887650909682, ...]}
res = client.insert(
collection_name="hybrid_search_collection",
data=data
)
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import io.milvus.v2.service.vector.request.InsertReq;
Gson gson = new Gson();
JsonObject row1 = new JsonObject();
row1.addProperty("id", 1);
row1.addProperty("text", "Artificial intelligence was founded as an academic discipline in 1956.");
row1.add("dense", gson.toJsonTree(dense1));
row1.add("sparse", gson.toJsonTree(sparse1));
JsonObject row2 = new JsonObject();
row2.addProperty("id", 2);
row2.addProperty("text", "Alan Turing was the first person to conduct substantial research in AI.");
row2.add("dense", gson.toJsonTree(dense2));
row2.add("sparse", gson.toJsonTree(sparse2));
JsonObject row3 = new JsonObject();
row3.addProperty("id", 3);
row3.addProperty("text", "Born in Maida Vale, London, Turing was raised in southern England.");
row3.add("dense", gson.toJsonTree(dense3));
row3.add("sparse", gson.toJsonTree(sparse3));
List<JsonObject> data = Arrays.asList(row1, row2, row3);
InsertReq insertReq = InsertReq.builder()
.collectionName("hybrid_search_collection")
.data(data)
.build();
InsertResp insertResp = client.insert(insertReq);
const { MilvusClient, DataType } = require("@zilliz/milvus2-sdk-node")
var data = [
{id: 0, text: "Artificial intelligence was founded as an academic discipline in 1956.", sparse:[9637: 0.30856525997853057, 4399: 0.19771651149001523, ...] , dense: [0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592]},
{id: 1, text: "Alan Turing was the first person to conduct substantial research in AI.", sparse:[6959: 0.31025067641541815, 1729: 0.8265339135915016, ...] , dense: [0.19886812562848388, 0.06023560599112088, 0.6976963061752597, 0.2614474506242501, 0.838729485096104]},
{id: 2, text: "Born in Maida Vale, London, Turing was raised in southern England." , sparse:[1220: 0.15303302147479103, 7335: 0.9436728846033107, ...] , dense: [0.43742130801983836, -0.5597502546264526, 0.6457887650909682, 0.7894058910881185, 0.20785793220625592]}
]
var res = await client.insert({
collection_name: "hybrid_search_collection",
data: data,
})
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/insert" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-d '{
"data": [
{"id": 0, "text": "Artificial intelligence was founded as an academic discipline in 1956.", "sparse":{"9637": 0.30856525997853057, "4399": 0.19771651149001523}, "dense": [0.3580376395471989, -0.6023495712049978, 0.18414012509913835, ...]},
{"id": 1, "text": "Alan Turing was the first person to conduct substantial research in AI.", "sparse":{"6959": 0.31025067641541815, "1729": 0.8265339135915016}, "dense": [0.19886812562848388, 0.06023560599112088, 0.6976963061752597, ...]},
{"id": 2, "text": "Born in Maida Vale, London, Turing was raised in southern England.", "sparse":{"1220": 0.15303302147479103, "7335": 0.9436728846033107}, "dense": [0.43742130801983836, -0.5597502546264526, 0.6457887650909682, ...]}
],
"collectionName": "hybrid_search_collection"
}'
Créer plusieurs instances AnnSearchRequest
La recherche hybride est mise en œuvre en créant plusieurs AnnSearchRequest
dans la fonction hybrid_search()
, où chaque AnnSearchRequest
représente une demande de recherche ANN de base pour un champ vectoriel spécifique. Par conséquent, avant d'effectuer une recherche hybride, il est nécessaire de créer un site AnnSearchRequest
pour chaque champ vectoriel.
Dans la recherche hybride, chaque site AnnSearchRequest
ne prend en charge qu'un seul vecteur de requête.
Supposons que le texte de la requête "Qui a lancé la recherche sur l'IA ?" ait déjà été converti en vecteurs épars et denses. Sur cette base, deux requêtes de recherche AnnSearchRequest
sont créées pour les champs vectoriels sparse
et dense
respectivement, comme le montre l'exemple suivant.
from pymilvus import AnnSearchRequest
query_dense_vector = [0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592]
search_param_1 = {
"data": [query_dense_vector],
"anns_field": "dense",
"param": {
"metric_type": "IP",
"params": {"nprobe": 10}
},
"limit": 2
}
request_1 = AnnSearchRequest(**search_param_1)
query_sparse_vector = {3573: 0.34701499565746674}, {5263: 0.2639375518635271}
search_param_2 = {
"data": [query_sparse_vector],
"anns_field": "sparse",
"param": {
"metric_type": "IP",
"params": {"drop_ratio_build": 0.2}
},
"limit": 2
}
request_2 = AnnSearchRequest(**search_param_2)
reqs = [request_1, request_2]
import io.milvus.v2.service.vector.request.AnnSearchReq;
import io.milvus.v2.service.vector.request.data.BaseVector;
import io.milvus.v2.service.vector.request.data.FloatVec;
import io.milvus.v2.service.vector.request.data.SparseFloatVec;
float[] dense = new float[]{-0.0475336798f, 0.0521207601f, 0.0904406682f, ...};
SortedMap<Long, Float> sparse = new TreeMap<Long, Float>() {{
put(3573L, 0.34701499f);
put(5263L, 0.263937551f);
...
}};
List<BaseVector> queryDenseVectors = Collections.singletonList(new FloatVec(dense));
List<BaseVector> querySparseVectors = Collections.singletonList(new SparseFloatVec(sparse));
List<AnnSearchReq> searchRequests = new ArrayList<>();
searchRequests.add(AnnSearchReq.builder()
.vectorFieldName("dense")
.vectors(queryDenseVectors)
.metricType(IndexParam.MetricType.IP)
.params("{\"nprobe\": 10}")
.topK(2)
.build());
searchRequests.add(AnnSearchReq.builder()
.vectorFieldName("sparse")
.vectors(querySparseVectors)
.metricType(IndexParam.MetricType.IP)
.params("{\"drop_ratio_build\": 0.2}")
.topK(2)
.build());
const search_param_1 = {
"data": query_vector,
"anns_field": "dense",
"param": {
"metric_type": "IP", // 参数值需要与 Collection Schema 中定义的保持一致
"params": {"nprobe": 10}
},
"limit": 2 // AnnSearchRequest 返还的搜索结果数量
}
const search_param_2 = {
"data": query_sparse_vector,
"anns_field": "sparse",
"param": {
"metric_type": "IP", // 参数值需要与 Collection Schema 中定义的保持一致
"params": {"drop_ratio_build": 0.2}
},
"limit": 2 // AnnSearchRequest 返还的搜索结果数量
}
export req='[
{
"data": [[0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592,....]],
"annsField": "dense",
"params": {
"params": {
"nprobe": 10
}
},
"limit": 2
},
{
"data": [{"3573": 0.34701499565746674}, {"5263": 0.2639375518635271}],
"annsField": "sparse",
"params": {
"params": {
"drop_ratio_build": 0.2
}
},
"limit": 2
}
]'
Comme le paramètre limit
est fixé à 2, chaque AnnSearchRequest
renvoie 2 résultats de recherche. Dans cet exemple, 2 AnnSearchRequest
sont créés, ce qui donne un total de 4 résultats de recherche.
Configurer une stratégie de reclassement
Pour fusionner et reclasser les deux ensembles de résultats de recherche ANN, il est nécessaire de sélectionner une stratégie de reclassement appropriée. Zilliz prend en charge deux types de stratégie de reclassement : WeightedRanker et RRFRanker. Lors du choix d'une stratégie de reclassement, il convient de se demander si l'accent est mis sur une ou plusieurs recherches ANN de base sur les champs vectoriels.
WeightedRanker: Cette stratégie est recommandée si vous souhaitez que les résultats mettent l'accent sur un champ vectoriel particulier. Le WeightedRanker vous permet d'attribuer des poids plus élevés à certains champs vectoriels, ce qui les met davantage en valeur. Par exemple, dans les recherches multimodales, les descriptions textuelles d'une image peuvent être considérées comme plus importantes que les couleurs de cette image.
RRFRanker (Reciprocal Rank Fusion Ranker): Cette stratégie est recommandée lorsqu'il n'y a pas d'importance particulière. Le RRF peut équilibrer efficacement l'importance de chaque champ vectoriel.
Pour plus de détails sur les mécanismes de ces deux stratégies de reclassement, reportez-vous à la section Reranking.
Les deux exemples suivants montrent comment utiliser les stratégies de reclassement WeightedRanker et RRFRanker.
Exemple 1 : utilisation de WeightedRanker
Lors de l'utilisation de la stratégie WeightedRanker, les valeurs de poids doivent être entrées dans la fonction
WeightedRanker
. Le nombre de recherches ANN de base dans une recherche hybride correspond au nombre de valeurs à entrer. Les valeurs saisies doivent être comprises dans l'intervalle [0,1], les valeurs proches de 1 indiquant une plus grande importance.from pymilvus import WeightedRanker rerank= WeightedRanker(0.8, 0.3)
import io.milvus.v2.service.vector.request.ranker.BaseRanker; import io.milvus.v2.service.vector.request.ranker.WeightedRanker; BaseRanker reranker = new WeightedRanker(Arrays.asList(0.8f, 0.3f));
import { MilvusClient, DataType } from "@zilliz/milvus2-sdk-node"; const rerank = WeightedRanker(0.8, 0.3);
export rerank='{ "strategy": "ws", "params": {"weights": [0.8,0.3]} }'
Exemple 2 : utilisation de RRFRanker
Lorsque vous utilisez la stratégie RRFRanker, vous devez saisir la valeur du paramètre
k
dans le RRFRanker. La valeur par défaut dek
est 60. Ce paramètre permet de déterminer comment les classements sont combinés à partir de différentes recherches ANN, afin d'équilibrer et de mélanger l'importance de toutes les recherches.from pymilvus import RRFRanker ranker = RRFRanker(100)
import io.milvus.v2.service.vector.request.ranker.BaseRanker; import io.milvus.v2.service.vector.request.ranker.RRFRanker; BaseRanker reranker = new RRFRanker(100);
import { MilvusClient, DataType } from "@zilliz/milvus2-sdk-node"; const rerank = RRFRanker("100");
export rerank='{ "strategy": "rrf", "params": { "k": 100} }'
Effectuer une recherche hybride
Avant d'effectuer une recherche hybride, il est nécessaire de charger la collection en mémoire. Si l'un des champs vectoriels de la collection n'a pas d'index ou n'est pas chargé, une erreur se produira lors de l'appel de la méthode de recherche hybride.
from pymilvus import MilvusClient
res = client.hybrid_search(
collection_name="hybrid_search_collection",
reqs=reqs,
ranker=ranker,
limit=2
)
for hits in res:
print("TopK results:")
for hit in hits:
print(hit)
import io.milvus.v2.common.ConsistencyLevel;
import io.milvus.v2.service.vector.request.HybridSearchReq;
import io.milvus.v2.service.vector.response.SearchResp;
HybridSearchReq hybridSearchReq = HybridSearchReq.builder()
.collectionName("hybrid_search_collection")
.searchRequests(searchRequests)
.ranker(reranker)
.topK(2)
.consistencyLevel(ConsistencyLevel.BOUNDED)
.build();
SearchResp searchResp = client.hybridSearch(hybridSearchReq);
const { MilvusClient, DataType } = require("@zilliz/milvus2-sdk-node")
res = await client.loadCollection({
collection_name: "hybrid_search_collection"
})
import { MilvusClient, RRFRanker, WeightedRanker } from '@zilliz/milvus2-sdk-node';
const search = await client.search({
collection_name: "hybrid_search_collection",
data: [search_param_1, search_param_2],
limit: 2,
rerank: RRFRanker(100)
});
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/advanced_search" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-d "{
\"collectionName\": \"hybrid_search_collection\",
\"search\": ${req},
\"rerank\": {
\"strategy\":\"rrf\",
\"params\": {
\"k\": 10
}
},
\"limit\": 3,
\"outputFields\": [
\"user_id\",
\"word_count\",
\"book_describe\"
]
}"
Le résultat est le suivant.
["['id: 844, distance: 0.006047376897186041, entity: {}', 'id: 876, distance: 0.006422005593776703, entity: {}']"]
Puisque limit=2
est spécifié dans la recherche hybride, Zilliz classera les quatre résultats de recherche de l'étape 3 et ne renverra finalement que les deux premiers résultats les plus similaires.