Accéder au contenu principal

Comment implémenter la recherche sémantique dans MongoDB

Apprenez à implémenter la recherche sémantique dans MongoDB avec Python. Générez des embeddings vectoriels, créez un index Vector Search et exécutez des requêtes $vectorSearch.
Actualisé 31 juil. 2026  · 9 min lire

Explorer avec l’IA

Ouvrir dans ChatGPTOuvrir dans ClaudeOuvrir dans Perplexity

Voici des situations où la recherche textuelle classique montre ses limites. Concrètement, cela donne :

  • Un portail d'assistance ne renvoie aucun résultat lorsqu'un utilisateur saisit « ne s'allume pas » au lieu de l'expression exacte utilisée dans l'article d'aide.
  • Une plateforme e-commerce ne trouve rien quand un client cherche « quelque chose de chaud pour l'hiver » parce qu'aucune fiche produit ne contient exactement ces mots.
  • Une base de connaissances passe à côté du bon document car l'utilisateur a formulé sa question différemment du libellé du document.

La recherche sémantique résout ce problème. Elle comprend le sens et l'intention derrière une requête plutôt que de faire correspondre des mots à l'identique.

Ce tutoriel vous montre comment implémenter une recherche sémantique dans MongoDB avec Python et le modèle d'embedding gratuit nomic-embed-text-v1.

Remarque : nomic-embed-text-v1 pèse environ 0,27 Go une fois chargé en mémoire. Assurez-vous que votre machine dispose d'au moins 1 Go de RAM disponible avant d'exécuter un script de ce tutoriel. Lors de la première exécution, le modèle est téléchargé depuis Hugging Face : prévoyez un délai supplémentaire selon votre connexion. Les exécutions suivantes chargent le modèle depuis le cache local. L'inférence sur CPU est nettement plus lente que sur GPU. Sur une machine sans GPU dédié, le modèle s'exécute sur CPU et utilise la RAM plutôt que la VRAM, ce qui peut rallonger la génération des embeddings.

Dans ce tutoriel, vous apprendrez :

  • Ce que sont les embeddings vectoriels et comment ils représentent le sens
  • Comment générer et stocker des embeddings dans MongoDB
  • Comment créer un index MongoDB Vector Search
  • Comment convertir une requête utilisateur en vecteur
  • Comment exécuter un pipeline d'agrégation $vectorSearch et interpréter les résultats

Vous trouverez tous les extraits de code de ce tutoriel dans le référentiel GitHub.

Recherche textuelle vs recherche sémantique

La recherche textuelle retrouve les documents qui contiennent exactement les mots de votre requête. La recherche sémantique retourne des documents qui portent le même sens que votre requête, même s'ils utilisent des mots complètement différents.

Le tableau suivant illustre ce que chaque approche retourne pour la requête « heart problems » :

Texte du document Recherche textuelle Recherche sémantique Pourquoi la recherche textuelle le retourne
"...heart problems in adults..." Oui Oui Contient exactement les mots « heart problems »
"...cardiac conditions and symptoms..." Non Oui Ne contient pas exactement les mots « heart problems »
"...risk factors for heart disease..." Non Oui Ne contient pas exactement les mots « heart problems »
"...chest pain and shortness of breath..." Non Oui Ne contient pas exactement les mots « heart problems »

Notions clés de la recherche sémantique

Trois concepts sont centraux pour la recherche sémantique dans MongoDB. Ils vous aideront à comprendre pourquoi chaque étape de ce tutoriel fonctionne comme elle le fait.

Embeddings vectoriels

Un modèle d'embedding convertit un texte en une liste de nombres de longueur fixe appelée vecteur. Chaque nombre du vecteur représente une dimension de sens.

Deux textes qui ont des sens proches produisent des vecteurs numériquement proches. « cardiac conditions » et « heart problems » se retrouvent voisins dans l'espace vectoriel, même s'ils ne partagent aucun mot. Cette proximité rend la recherche sémantique possible.

Ce tutoriel utilise le modèle d'embedding nomic-embed-text-v1, gratuit, open source et entièrement exécutable en local. Le modèle est automatiquement téléchargé depuis Hugging Face lors de la première exécution, puis mis en cache localement pour les suivantes. Le modèle produit 768 nombres par entrée.

Index de recherche vectorielle

Un index de recherche vectorielle indique à MongoDB quel champ contient les embeddings, combien de dimensions attendre et quelle fonction de similarité utiliser pour la comparaison. Vous devez créer cet index avant d'exécuter des requêtes $vectorSearch.

L'étape d'agrégation $vectorSearch

$vectorSearch est l'étape d'agrégation qui exécute la recherche. Elle accepte un vecteur de requête, parcourt le champ indexé et renvoie les documents classés par similarité sémantique. Vous pouvez l'enchaîner avec $project et d'autres étapes comme dans n'importe quel pipeline d'agrégation.

Allons-y.

Prérequis

Avant de commencer, assurez-vous d'avoir :

  • Python 3.8 ou version ultérieure installé
  • Un compte MongoDB Atlas avec un cluster M0 (offre Free tier) configuré
  • pymongo 4.7 ou ultérieur, et les packages Python sentence-transformers et einops installés
  • Des notions de base en Python et sur les collections MongoDB
  • Votre chaîne de connexion Atlas, disponible dans l'interface Atlas sous **Database > Connect > Drivers**
  • Votre adresse IP autorisée dans Atlas avant d'exécuter des scripts. Allez dans **Security > Network Access** dans l'interface Atlas et ajoutez votre adresse IP actuelle.

Configurer votre projet

Créez un dossier pour votre projet et placez-vous dedans via le terminal :

mkdir mongodb-semantic-search
cd mongodb-semantic-search

Créez et activez un environnement virtuel pour isoler les dépendances du projet :

python -m venv venv
source venv/bin/activate

Sous Windows, activez l'environnement virtuel avec :

venv\Scripts\activate

Installez maintenant les packages requis :

pip install pymongo sentence-transformers einops

Vous allez créer un fichier Python pour chaque étape de ce tutoriel. Tous les fichiers iront dans le dossier mongodb-semantic-search.

Créer le fichier d'utilitaires d'embedding

Créez un fichier nommé embedding_utils.py. Ce fichier contient les deux fonctions d'embedding utilisées dans ce tutoriel :

from sentence_transformers import SentenceTransformer

# Load the free, open-source embedding model.
# The model downloads from Hugging Face on first run and saves locally.
# trust_remote_code=True is required by this model:
model = SentenceTransformer("nomic-ai/nomic-embed-text-v1", trust_remote_code=True)


def get_embedding(text, precision="float32"):
    # Use this function when embedding text you plan to store in MongoDB:
    return model.encode(text, precision=precision).tolist()


def get_query_embedding(text, precision="float32"):
    # Use this function when embedding a user's search query:
    return model.encode(text, precision=precision).tolist()

Avertissement : trust_remote_code=True autorise l'exécution sur votre machine de code Python spécifique au modèle et téléchargé depuis Hugging Face. Si le code source du modèle sur Hugging Face est compromis ou contient des modifications malveillantes, ce code s'exécutera automatiquement dans votre environnement. Faites preuve de prudence avant d'utiliser ce paramètre en production.

Générer et stocker les embeddings vectoriels

Créez un fichier nommé generate_embeddings.py. Ce fichier importe la fonction d'embedding depuis embedding_utils.py, génère un vecteur pour le champ texte de chaque document et stocke le document complet, y compris l'embedding, dans MongoDB :

from embedding_utils import get_embedding
from pymongo import MongoClient

# Replace the placeholder with your Atlas connection string:
mongodb_client = MongoClient(
    "mongodb+srv://<USERNAME>:<PASSWORD>@<HOST>/",
    appname="devrel-tutorial-python-semantic-search"
)

collection = mongodb_client["sample_db"]["documents"]

# Sample data:
sample_documents = [
    {
        "title": "MongoDB Atlas",
        "text": "MongoDB Atlas is a fully managed cloud database."
    },
    {
        "title": "Vector Search",
        "text": "Vector search finds results based on semantic meaning."
    },
    {
        "title": "Nomic AI",
        "text": "nomic-embed-text-v1 is a free, open-source embedding model."
    },
]

docs_to_insert = []

for doc in sample_documents:
    embedding = get_embedding(doc["text"])
    docs_to_insert.append({
        "title": doc["title"],
        "text": doc["text"],
        # The vector lives alongside your original data in the same document:
        "embedding": embedding
    })

# Drop the collection before each run to avoid inserting duplicate documents:
collection.drop()

result = collection.insert_many(docs_to_insert)
print(f"Inserted {len(result.inserted_ids)} documents with embeddings.")

mongodb_client.close()

Dans le code ci-dessus, collection.drop() supprime tous les documents et index de la collection avant chaque exécution. Relancer generate_embeddings.py sans cette ligne insère des doublons, ce qui amène $vectorSearch à retourner plusieurs fois le même document. La suppression de la collection efface aussi l'index de recherche vectorielle : vous devrez donc relancer create_vector_index.py à chaque fois que vous relancez generate_embeddings.py.

Exécutez le script :

python generate_embeddings.py

Vous verrez la sortie suivante dans votre terminal :

<All keys matched successfully>
Inserted 3 documents with embeddings.

Chaque document dans MongoDB contient désormais à la fois le texte d'origine et son vecteur à 768 dimensions dans le champ embedding. Le vecteur vit aux côtés de vos données, dans le même document : aucun stockage séparé ni recherche additionnelle n'est nécessaire au moment de la requête.

Créer un index MongoDB Vector Search

Créez un fichier nommé create_vector_index.py. Ce fichier définit et crée un index de recherche vectorielle sur le champ embedding afin que MongoDB puisse exécuter des requêtes $vectorSearch sur votre collection.

La définition de l'index requiert trois champs :

  • path : le champ qui contient les embeddings (embedding dans ce tutoriel)
  • numDimensions : doit correspondre à la taille de sortie du modèle (768 pour nomic-embed-text-v1)
  • similarity : la fonction de comparaison (cosine convient à ce modèle)

La valeur de numDimensions doit correspondre exactement à celle de votre modèle d'embedding. En cas d'écart, la création de l'index échoue :

from pymongo.mongo_client import MongoClient
from pymongo.operations import SearchIndexModel
import time

# Replace the placeholder with your Atlas connection string:
mongodb_client = MongoClient(
    "mongodb+srv://<USERNAME>:<PASSWORD>@<HOST>/",
    appname="devrel-tutorial-python-semantic-search"
)

# Point to the same database and collection you used in the previous step:
database = mongodb_client["sample_db"]
collection = database["documents"]

# Define the vector search index.
# The three required fields tell MongoDB what to index and how to compare vectors:
search_index_model = SearchIndexModel(
    definition={
        "fields": [
            {
                "type": "vector",
                "path": "embedding",      # The field name from generate_embeddings.py
                "numDimensions": 768,     # nomic-embed-text-v1 always outputs 768 dimensions
                "similarity": "cosine"    # Recommended similarity function for this model
            }
        ]
    },
    name="vector_index",
    type="vectorSearch"
)

result = collection.create_search_index(model=search_index_model)
print("New search index named " + result + " is building.")

# Poll every five seconds until the index is ready to accept queries:
print("Polling to check if the index is ready. This may take up to a minute.")

predicate = lambda index: index.get("queryable") is True

while True:
    indices = list(collection.list_search_indexes(result))
    if len(indices) and predicate(indices[0]):
        break
    time.sleep(5)

print(result + " is ready for querying.")

mongodb_client.close()

Exécutez le script :

python create_vector_index.py

Vous verrez la sortie suivante dans votre terminal :

New search index named vector_index is building.
Polling to check if the index is ready. This may take up to a minute.
vector_index is ready for querying.

La construction de l'index peut prendre jusqu'à une minute. La boucle d'interrogation vérifie toutes les 5 secondes et ne s'arrête que lorsque MongoDB confirme que l'index est interrogeable. N'avancez pas tant que vous n'avez pas vu « vector_index is ready for querying. »

Convertir votre requête de recherche en vecteur

Créez un fichier nommé generate_query_vector.py. Ce fichier importe la fonction get_query_embedding() depuis embedding_utils.py. Exécutez generate_query_vector.py pour vérifier que le modèle d'embedding se charge correctement et produit un vecteur à 768 dimensions :

  • Il définit la fonction get_query_embedding() qui sera importée à l'étape suivante.
  • Vous pouvez l'exécuter seul pour vérifier que le modèle fonctionne correctement.

Vous devez utiliser le même modèle ici que lors de la première étape. Un modèle différent produit des vecteurs dans un espace numérique différent, rendant la comparaison inutile :

from embedding_utils import get_query_embedding

user_query = "I need an automated, scalable system for serious information storage"
query_vector = get_query_embedding(user_query)
print(f"Query: '{user_query}'")
print(f"Vector dimensions: {len(query_vector)}")
print(f"First 5 values: {query_vector[:5]}")

Exécutez le script :

python generate_query_vector.py

Vous obtiendrez une sortie semblable à celle-ci :

<All keys matched successfully>
Query: 'I need an automated, scalable system for serious information storage'
Vector dimensions: 768
First 5 values: [0.0025914530269801617, 0.09862980246543884, -0.023092379793524742, -0.0171672236174345, -0.05548065900802612]

Dans la sortie ci-dessus, Vector dimensions: 768 confirme que la sortie du modèle correspond à vos embeddings stockés.

Exécuter une requête $vectorSearch

Créez un fichier nommé run_vector_search.py. Ce fichier importe la fonction get_query_embedding() depuis embedding_utils.py et convertit le terme recherché par l'utilisateur en vecteur. run_vector_search.py exécute un pipeline d'agrégation $vectorSearch et affiche les résultats classés par similarité sémantique :

from embedding_utils import get_query_embedding
from pymongo import MongoClient

# Replace the placeholder with your Atlas connection string:
mongodb_client = MongoClient(
    "mongodb+srv://<USERNAME>:<PASSWORD>@<HOST>/",
    appname="devrel-tutorial-python-semantic-search"
)

collection = mongodb_client["sample_db"]["documents"]

# Generate a query vector from the user's search input:
user_query = "I need an automated, scalable system for serious information storage"
query_vector = get_query_embedding(user_query)

# Define the $vectorSearch aggregation pipeline:
pipeline = [
    {
        "$vectorSearch": {
            "index": "vector_index",      # The index created in create_vector_index.py
            "path": "embedding",          # The field that holds your stored vectors
            "queryVector": query_vector,  # The vector generated from the user's query
            "numCandidates": 150,         # How many neighbors MongoDB considers
            "limit": 3                    # How many results to return
        }
    },
    {
        "$project": {
            "_id": 0,
            "title": 1,
            "text": 1,
            "score": {
                "$meta": "vectorSearchScore"  # Relevance score for each result
            }
        }
    }
]

results = collection.aggregate(pipeline)

print(f"\nTop results for query: '{user_query}'\n")

for doc in results:
    print(f"Title: {doc['title']}")
    print(f"Text:  {doc['text']}")
    print(f"Score: {doc['score']:.4f}")
    print()

mongodb_client.close()

Dans le code ci-dessus, deux paramètres pilotent le comportement de la recherche :

  • numCandidates définit la taille du premier ensemble de candidats que MongoDB examine avant de restreindre les résultats finaux. Une valeur plus élevée améliore le rappel, au prix d'un temps de requête légèrement supérieur.
  • limit définit le nombre de résultats renvoyés. Dans ce tutoriel, limit est fixé à 3 et numCandidates à 150. Un bon point de départ consiste à régler numCandidates à 10 à 15 fois votre limit.

Exécutez maintenant le script :

python run_vector_search.py

Vous obtiendrez une sortie similaire à celle-ci :

<All keys matched successfully>

Top results for query: 'I need an automated, scalable system for serious information storage.'

Title: MongoDB Atlas
Text:  MongoDB Atlas is a fully managed cloud database.
Score: 0.7211

Title: Nomic AI
Text:  nomic-embed-text-v1 is a free, open-source embedding model.
Score: 0.6818

Title: Vector Search
Text:  Vector search finds results based on semantic meaning.
Score: 0.6642

« MongoDB Atlas » obtient le meilleur score, même si la requête « I need an automated, scalable system for serious information storage » ne partage aucun mot avec « MongoDB Atlas is a fully managed cloud database. » Le résultat est retourné car leurs vecteurs sont proches en termes de sens : « ...un système automatisé et scalable de stockage d'informations » correspond sémantiquement à « ...une base de données cloud entièrement managée ». Une recherche textuelle avec la même requête renverrait zéro résultat, puisque aucun mot exact n'apparaît dans les documents. C'est précisément le fonctionnement attendu de la recherche sémantique.

Points clés à retenir

  • MongoDB Vector Search fonctionne sur un cluster M0 Free tier. Aucun plan payant n'est requis.
  • Vous devez utiliser le même modèle d'embedding pour générer les embeddings stockés et les vecteurs de requête. Mélanger les modèles produit des résultats dénués de sens.
  • La valeur numDimensions dans la définition de votre index doit correspondre exactement à la taille de sortie de votre modèle d'embedding. nomic-embed-text-v1 produit toujours 768 dimensions.
  • input_type="document" optimise les embeddings pour le stockage. input_type="query" les optimise pour la recherche. Utilisez le bon type à chaque étape.
  • numCandidates contrôle l'ampleur du filet de recherche que MongoDB jette avant de restreindre aux résultats finaux définis par limit. Une valeur plus élevée améliore le rappel au détriment du temps de requête.
  • vectorSearchScore classe les résultats par similarité sémantique. Les résultats n'ont pas besoin de contenir les mots exacts de la requête. Les plages de score varient selon le modèle et le jeu de données, mais les seuils suivants constituent un bon point de départ pour nomic-embed-text-v1 avec la similarité cosinus :
    • 0,9 et plus : sens quasi identique. Le document et la requête sont presque équivalents sémantiquement.
    • 0,7 à 0,9 : forte pertinence. Le document correspond clairement à l'intention de la requête.
    • 0,5 à 0,7 : pertinence modérée. Le document est lié au sujet mais avec un cadrage ou un contexte différent.
    • En dessous de 0,5 : faible pertinence. Le lien est ténu et le résultat peut être peu utile.

Vous trouverez tous les extraits de code de ce tutoriel dans le référentiel GitHub.

Pour aller plus loin

FAQ

Ai-je besoin d'un abonnement Atlas payant pour implémenter la recherche sémantique ?

Non. Les quatre étapes de ce tutoriel s'exécutent sur un cluster M0 Free tier, qui est gratuit.

Que se passe-t-il si j'utilise un autre modèle d'embedding pour ma requête que pour mes documents stockés ?

Vos résultats seront dénués de sens. Les vecteurs ne seront pas comparables, car différents modèles projettent le texte dans des espaces numériques distincts. Utilisez toujours le même modèle pour l'indexation et la requête.

Quelle est la différence entre `numCandidates` et `limit` ?

numCandidates correspond au nombre de vecteurs que MongoDB examine pendant la recherche. limit est le nombre de meilleurs résultats retournés. Une valeur plus élevée de numCandidates améliore la qualité des résultats, avec des requêtes légèrement plus lentes. Un bon point de départ est de régler numCandidates à 10 à 15 fois votre limit.

Puis-je utiliser la recherche sémantique et la recherche textuelle ensemble ?

Oui. MongoDB prend en charge la recherche hybride, qui combine $vectorSearch et $search dans un seul pipeline.

La recherche sémantique fonctionne-t-elle pour des langues autres que l'anglais ?

Cela dépend de votre modèle d'embedding. nomic-embed-text-v1 est entraîné principalement sur des textes en anglais. Pour des cas d'usage multilingues, choisissez un modèle d'embedding multilingue entraîné sur les langues présentes dans vos données.


Damilola Oladele's photo
Author
Damilola Oladele
Sujets