Requêtes Elasticsearch vers Milvus

Elasticsearch, basé sur Apache Lucene, est un moteur de recherche open-source de premier plan. Cependant, il est confronté à des défis dans les applications d'IA modernes, notamment des coûts de mise à jour élevés, des performances en temps réel médiocres, une gestion inefficace des blocs de données, une conception non native dans le nuage et des demandes de ressources excessives. En tant que base de données vectorielle native, Milvus surmonte ces problèmes grâce au stockage et au calcul découplés, à l'indexation efficace des données à haute dimension et à l'intégration transparente avec les infrastructures modernes. Il offre des performances et une évolutivité supérieures pour les charges de travail d'IA.

Cet article vise à faciliter la migration de votre base de code d'Elasticsearch vers Milvus, en fournissant divers exemples de conversion de requêtes entre les deux.

Vue d'ensemble

Dans Elasticsearch, les opérations dans le contexte de la requête génèrent des scores de pertinence, alors que celles dans le contexte du filtre ne le font pas. De même, les recherches Milvus produisent des scores de similarité, alors que ses requêtes de type filtre n'en produisent pas. Lors de la migration de votre base de code d'Elasticsearch vers Milvus, le principe clé consiste à convertir les champs utilisés dans le contexte de requête d'Elasticsearch en champs vectoriels pour permettre la génération de scores de similarité.

Le tableau ci-dessous présente certains modèles de requête Elasticsearch et leurs équivalents correspondants dans Milvus.

Requêtes Elasticsearch

Équivalents Milvus

Remarques

Requêtes en texte intégral

Requête de correspondance

Recherche en texte intégral

Les deux offrent des fonctionnalités similaires.

Requêtes au niveau des termes

ID

in opérateur

Ces deux types de requêtes offrent des fonctionnalités identiques ou similaires lorsque ces requêtes Elasticsearch sont utilisées dans le contexte d'un filtre.

Requête par préfixe

like opérateur

Requête de plage

Opérateurs de comparaison tels que >, <, >=, et <=

Requête par terme

Opérateurs de comparaison comme ==

Requête de termes

in opérateur

L'opérateur de recherche

like opérateur

Requête booléenne

Opérateurs logiques comme AND

Ces deux opérateurs offrent des possibilités similaires lorsqu'ils sont utilisés dans le contexte d'un filtre.

Requêtes vectorielles

Requête kNN

Recherche

Milvus offre des fonctionnalités de recherche vectorielle plus avancées.

Fusion réciproque des rangs

Recherche hybride

Milvus prend en charge plusieurs stratégies de reclassement.

Requêtes en texte intégral

Dans Elasticsearch, les requêtes en texte intégral vous permettent de rechercher des champs de texte analysés tels que le corps d'un courrier électronique. La chaîne de requête est traitée à l'aide du même analyseur que celui appliqué au champ lors de l'indexation.

Requête de correspondance

Dans Elasticsearch, une requête de correspondance renvoie les documents qui correspondent à un texte, un nombre, une date ou une valeur booléenne. Le texte fourni est analysé avant la mise en correspondance.

Voici un exemple de requête de recherche Elasticsearch avec une requête de correspondance.

resp = client.search(
    query={
        "match": {
            "message": {
                "query": "this is a test"
            }
        }
    },
)

Milvus offre la même possibilité grâce à la fonction de recherche en texte intégral. Vous pouvez convertir la requête Elasticsearch ci-dessus dans Milvus comme suit :

res = client.search(
    collection_name="my_collection",
    data=['How is the weather in Jamaica?'],
    anns_field="message_sparse",
    output_fields=["id", "message"]
)

Dans l'exemple ci-dessus, message_sparse est un champ de vecteurs clairsemés dérivé d'un champ VarChar nommé message. Milvus utilise le modèle d'intégration BM25 pour convertir les valeurs du champ message en intégrations de vecteurs éparses et les stocke dans le champ message_sparse. Lors de la réception de la demande de recherche, Milvus incorpore la charge utile de la requête en texte brut à l'aide du même modèle BM25 et effectue une recherche de vecteur clair et renvoie les champs id et message spécifiés dans le paramètre output_fields ainsi que les scores de similarité correspondants.

Pour utiliser cette fonctionnalité, vous devez activer l'analyseur sur le champ message et définir une fonction pour en déduire le champ message_sparse. Pour obtenir des instructions détaillées sur l'activation de l'analyseur et la création de la fonction dérivée dans Milvus, reportez-vous à la section Recherche en texte intégral.

Requêtes au niveau des termes

Dans Elasticsearch, les requêtes au niveau du terme sont utilisées pour trouver des documents basés sur des valeurs exactes dans des données structurées, telles que des plages de dates, des adresses IP, des prix ou des identifiants de produits. Cette section présente les équivalents possibles de certaines requêtes de niveau terme d'Elasticsearch dans Milvus. Tous les exemples de cette section sont adaptés pour fonctionner dans le contexte du filtre afin de s'aligner sur les capacités de Milvus.

ID

Dans Elasticsearch, vous pouvez rechercher des documents en fonction de leur ID dans le contexte du filtre comme suit :

resp = client.search(
    query={
        "bool": {
            "filter": {
                "ids": {
                    "values": [
                        "1",
                        "4",
                        "100"
                    ]
                }            
            }
        }
    },
)

Dans Milvus, vous pouvez également trouver des entités sur la base de leurs ID comme suit :

# Use the filter parameter
res = client.query(
    collection_name="my_collection",
    filter="id in [1, 4, 100]",
    output_fields=["id", "title"]
)

# Use the ids parameter
res = client.query(
    collection_name="my_collection",
    ids=[1, 4, 100],
    output_fields=["id", "title"]
)

Vous trouverez l'exemple Elasticsearch sur cette page. Pour plus de détails sur les requêtes et les demandes d'obtention ainsi que sur les expressions de filtrage dans Milvus, reportez-vous à Requête et filtrage.

Requête de préfixe

Dans Elasticsearch, vous pouvez rechercher des documents qui contiennent un préfixe spécifique dans un champ fourni dans le contexte de filtrage comme suit :

resp = client.search(
    query={
        "bool": {
            "filter": {
                 "prefix": {
                    "user": {
                        "value": "ki"
                    }
                }           
            }
        }
    },
)

Dans Milvus, vous pouvez trouver les entités dont les valeurs commencent par le préfixe spécifié comme suit :

res = client.query(
    collection_name="my_collection",
    filter='user like "ki%"',
    output_fields=["id", "user"]
)

Vous trouverez l'exemple Elasticsearch sur cette page. Pour plus de détails sur l'opérateur like dans Milvus, reportez-vous à la section Utilisation de LIKE pour la recherche de motifs.

Requête de plage

Dans Elasticsearch, vous pouvez rechercher des documents qui contiennent des termes dans une plage donnée, comme suit : Dans Milvus, vous pouvez rechercher des documents qui contiennent des termes dans une plage donnée :

resp = client.search(
    query={
        "bool": {
            "filter": {
                "range": {
                    "age": {
                        "gte": 10,
                        "lte": 20
                    }
                }           
            }
        }
    },
)

Dans Milvus, vous pouvez rechercher les entités dont les valeurs d'un champ spécifique se situent dans un intervalle donné, comme suit :

res = client.query(
    collection_name="my_collection",
    filter='10 <= age <= 20',
    output_fields=["id", "user", "age"]
)

Vous trouverez l'exemple Elasticsearch sur cette page. Pour plus de détails sur les opérateurs de comparaison dans Milvus, voir Opérateurs de comparaison.

Requête de terme

Dans Elasticsearch, vous pouvez rechercher des documents qui contiennent un terme exact dans un champ fourni, comme suit :

resp = client.search(
    query={
        "bool": {
            "filter": {
                "term": {
                    "status": {
                        "value": "retired"
                    }
                }            
            }
        }
    },
)

Dans Milvus, vous pouvez rechercher les entités dont les valeurs dans le champ spécifié correspondent exactement au terme spécifié comme suit :

# use ==
res = client.query(
    collection_name="my_collection",
    filter='status=="retired"',
    output_fields=["id", "user", "status"]
)

# use TEXT_MATCH
res = client.query(
    collection_name="my_collection",
    filter='TEXT_MATCH(status, "retired")',
    output_fields=["id", "user", "status"]
)

Vous trouverez l'exemple Elasticsearch sur cette page. Pour plus de détails sur les opérateurs de comparaison dans Milvus, voir Opérateurs de comparaison.

Requête de termes

Dans Elasticsearch, vous pouvez rechercher des documents qui contiennent un ou plusieurs termes exacts dans un champ fourni, comme suit :

resp = client.search(
    query={
        "bool": {
            "filter": {
                "terms": {
                    "degree": [
                        "graduate",
                        "post-graduate"
                    ]
                }        
            }
        }
    }
)

Milvus n'a pas d'équivalence complète de celle-ci. Cependant, vous pouvez trouver les entités dont les valeurs dans le champ spécifié sont l'un des termes spécifiés comme suit :

# use in
res = client.query(
    collection_name="my_collection",
    filter='degree in ["graduate", "post-graduate"]',
    output_fields=["id", "user", "degree"]
)

# use TEXT_MATCH
res = client.query(
    collection_name="my_collection",
    filter='TEXT_MATCH(degree, "graduate post-graduate")',
    output_fields=["id", "user", "degree"]
)

Vous trouverez l'exemple Elasticsearch sur cette page. Pour plus de détails sur les opérateurs de plage dans Milvus, reportez-vous à Opérateurs de plage.

Requête de caractères génériques

Dans Elasticsearch, vous pouvez rechercher des documents qui contiennent des termes correspondant à un motif de caractère générique, comme suit : Milvus ne prend pas en charge les caractères génériques :

resp = client.search(
    query={
        "bool": {
            "filter": {
                "wildcard": {
                    "user": {
                        "value": "ki*y"
                    }
                }          
            }
        }
    },
)

Milvus ne prend pas en charge les caractères génériques dans ses conditions de filtrage. Cependant, vous pouvez utiliser l'opérateur like pour obtenir un effet similaire, comme suit :

res = client.query(
    collection_name="my_collection",
    filter='user like "ki%" AND user like "%y"',
    output_fields=["id", "user"]
)

Vous trouverez l'exemple Elasticsearch sur cette page. Pour plus de détails sur les opérateurs de plage dans Milvus, reportez-vous à Opérateurs de plage.

Requête booléenne

Dans Elasticsearch, une requête booléenne est une requête qui fait correspondre des documents à des combinaisons booléennes d'autres requêtes.

L'exemple suivant est adapté d'un exemple de la documentation Elasticsearch sur cette page. La requête renvoie les utilisateurs dont le nom contient kimchy avec une balise production.

resp = client.search(
    query={
        "bool": {
            "filter": {
                "term": {
                    "user": "kimchy"
                }
            },
            "filter": {
                "term": {
                    "tags": "production"
                }
            }
        }
    },
)

Dans Milvus, vous pouvez faire la même chose comme suit :

filter = 

res = client.query(
    collection_name="my_collection",
    filter='user like "%kimchy%" AND ARRAY_CONTAINS(tags, "production")',
    output_fields=["id", "user", "age", "tags"]
)

L'exemple ci-dessus suppose que vous disposez d'un champ user de type VarChar et d'un champ tags de type Array dans la collection cible. La requête renverra les utilisateurs dont le nom contient kimchy avec une balise production.

Requêtes vectorielles

Dans Elasticsearch, les requêtes vectorielles sont des requêtes spécialisées qui utilisent des champs vectoriels pour effectuer une recherche sémantique efficace.

Requête Knn

Elasticsearch prend en charge les requêtes kNN approximatives et les requêtes kNN exactes à force brute. Vous pouvez trouver les k vecteurs les plus proches d'un vecteur de requête de l'une ou l'autre manière, mesurés par une métrique de similarité, comme suit :

resp = client.search(
    index="my-image-index",
    size=3,
    query={
        "knn": {
            "field": "image-vector",
            "query_vector": [
                -5,
                9,
                -12
            ],
            "k": 10
        }
    },
)

Milvus, en tant que base de données vectorielle spécialisée, utilise des types d'index pour optimiser les recherches vectorielles. En règle générale, il donne la priorité à la recherche approximative du plus proche voisin (ANN) pour les données vectorielles de haute dimension. Bien que la recherche kNN par force brute avec le type d'index FLAT produise des résultats précis, elle est à la fois longue et gourmande en ressources. En revanche, la recherche ANN utilisant AUTOINDEX ou d'autres types d'index permet d'équilibrer la vitesse et la précision, offrant des performances nettement plus rapides et plus économes en ressources que le kNN.

Une équivalence similaire à la requête vectorielle ci-dessus dans Mlivus se présente comme suit :

res = client.search(
    collection_name="my_collection",
    anns_field="image-vector"
    data=[[-5, 9, -12]],
    limit=10
)

Vous trouverez l'exemple Elasticsearch sur cette page. Pour plus de détails sur les recherches ANN dans Milvus, lisez Basic ANN Search.

Fusion de rangs réciproques

Elasticsearch propose la fonction Reciprocal Rank Fusion (RRF) pour combiner plusieurs ensembles de résultats avec différents indicateurs de pertinence en un seul ensemble de résultats classés.

L'exemple suivant illustre la combinaison d'une recherche traditionnelle basée sur les termes avec une recherche vectorielle k-nearest neighbors (kNN) pour améliorer la pertinence de la recherche :

client.search(
    index="my_index",
    size=10,
    query={
        "retriever": {
            "rrf": {
                "retrievers": [
                    {
                        "standard": {
                            "query": {
                                "term": {
                                    "text": "shoes"
                                }
                            }
                        }
                    },
                    {
                        "knn": {
                            "field": "vector",
                            "query_vector": [1.25, 2, 3.5],  # Example vector; replace with your actual query vector
                            "k": 50,
                            "num_candidates": 100
                        }
                    }
                ],
                "rank_window_size": 50,
                "rank_constant": 20
            }
        }
    }
)

Dans cet exemple, RRF combine les résultats de deux moteurs de recherche :

  • Une recherche standard basée sur les termes pour les documents contenant le terme "shoes" dans le champ text.

  • Une recherche kNN sur le champ vector à l'aide du vecteur de requête fourni.

Chaque moteur de recherche fournit jusqu'à 50 correspondances, qui sont reclassées par RRF, et les 10 premiers résultats finaux sont renvoyés.

Dans Milvus, vous pouvez réaliser une recherche hybride similaire en combinant des recherches sur plusieurs champs vectoriels, en appliquant une stratégie de reclassement et en récupérant les K premiers résultats de la liste combinée. Milvus prend en charge les stratégies RRF et reranker pondéré. Pour plus de détails, voir Reranking.

L'exemple suivant est une équivalence non stricte de l'exemple Elasticsearch ci-dessus dans Milvus.

search_params_dense = {
    "data": [[1.25, 2, 3.5]],
    "anns_field": "vector",
    "param": {
        "metric_type": "IP",
        "params": {"nprobe": 10},
    },
    "limit": 100
}

req_dense = ANNSearchRequest(**search_params_dense)

search_params_sparse = {
    "data": ["shoes"],
    "anns_field": "text_sparse",
    "param": {
        "metric_type": "BM25",
    }
}

req_sparse = ANNSearchRequest(**search_params_sparse)

res = client.hybrid_search(
    collection_name="my_collection",
    reqs=[req_dense, req_sparse],
    reranker=RRFRanker(),
    limit=10
)

Cet exemple démontre une recherche hybride dans Milvus qui combine :

  1. Recherche vectorielle dense: Utilisation de la métrique du produit intérieur (IP) avec nprobe fixé à 10 pour la recherche approximative du plus proche voisin (ANN) sur le champ vector.

  2. Recherche de vecteurs épars: Utilisation de la métrique de similarité BM25 sur le champ text_sparse.

Les résultats de ces recherches sont exécutés séparément, combinés et reclassés à l'aide du classificateur Reciprocal Rank Fusion (RRF). La recherche hybride renvoie les 10 premières entités de la liste reclassée.

Contrairement au classement RRF d'Elasticsearch, qui fusionne les résultats des requêtes textuelles standard et des recherches kNN, Milvus combine les résultats des recherches vectorielles éparses et denses, offrant ainsi une capacité de recherche hybride unique optimisée pour les données multimodales.

Récapitulatif

Dans cet article, nous avons couvert les conversions de requêtes Elasticsearch typiques en leurs équivalents Milvus, y compris les requêtes au niveau des termes, les requêtes booléennes, les requêtes en texte intégral et les requêtes vectorielles. Si vous avez d'autres questions sur la conversion d'autres requêtes Elasticsearch, n'hésitez pas à nous contacter.