Acestea sunt momentele în care căutarea text obișnuită nu mai face față. Iată cum arată asta în practică:
- Un portal de suport clienți nu returnează rezultate când un utilizator scrie „nu se aprinde” în locul expresiei exacte folosite în articolul de ajutor.
- O platformă de e-commerce nu găsește nimic când un cumpărător caută „ceva călduros pentru iarnă”, pentru că nicio descriere de produs nu conține exact acele cuvinte.
- O bază de cunoștințe ratează documentul corect pentru că utilizatorul și-a formulat întrebarea diferit față de cum a fost scris documentul.
Căutarea semantică rezolvă asta. Ea înțelege sensul și intenția din spatele interogării, nu doar potrivește cuvinte exacte.
Acest tutorial îți arată cum să implementezi căutarea semantică în MongoDB folosind Python și modelul de embedding gratuit nomic-embed-text-v1.
Notă: nomic-embed-text-v1 ocupă aproximativ 0,27 GB când este încărcat în memorie, așa că asigură-te că mașina ta are cel puțin 1 GB RAM disponibil înainte de a rula orice script din acest tutorial. Prima rulare descarcă modelul de pe Hugging Face, așa că alocă timp suplimentar în funcție de conexiunea ta la internet. Toate rulările ulterioare încarcă modelul din cache-ul local. Inferența pe CPU este semnificativ mai lentă decât pe GPU. Pe mașinile fără GPU dedicat, modelul rulează pe CPU și folosește RAM în loc de VRAM, așa că generarea embeddingurilor poate dura mai mult.
În acest tutorial vei învăța:
- Ce sunt embeddingurile vectoriale și cum reprezintă sensul
- Cum să generezi și să stochezi embeddinguri în MongoDB
- Cum să creezi un index MongoDB Vector Search
- Cum să convertești interogarea unui utilizator într-un vector
- Cum să rulezi o interogare
$vectorSearchîntr-un pipeline de agregare și cum să interpretezi rezultatele
Poți găsi toate mostrele de cod pentru acest tutorial în repository-ul GitHub.
Căutare text vs căutare semantică
Căutarea text potrivește documente care conțin exact cuvintele din interogarea ta. Căutarea semantică potrivește documente care poartă același sens cu interogarea ta, chiar dacă folosesc cuvinte complet diferite.
Tabelul de mai jos arată ce returnează fiecare abordare pentru interogarea „probleme la inimă”:
| Textul documentului | Căutare text | Căutare semantică | De ce o returnează căutarea text |
|---|---|---|---|
| "...probleme la inimă la adulți..." | Da | Da | Conține cuvintele exacte „probleme la inimă” |
| "...afecțiuni cardiace și simptome..." | Nu | Da | Nu conține cuvintele exacte „probleme la inimă” |
| "...factori de risc pentru boala cardiacă..." | Nu | Da | Nu conține cuvintele exacte „probleme la inimă” |
| "...dureri în piept și senzație de lipsă de aer..." | Nu | Da | Nu conține cuvintele exacte „probleme la inimă” |
Concepte de căutare semantică
Trei concepte sunt centrale pentru căutarea semantică în MongoDB. Te vor ajuta să înțelegi de ce fiecare pas din acest tutorial funcționează așa cum funcționează.
Embeddinguri vectoriale
Un model de embedding convertește textul într-o listă de numere cu lungime fixă, numită vector. Fiecare număr din vector reprezintă o dimensiune a sensului.
Două texte cu sensuri similare produc vectori numeric apropiați. „afecțiuni cardiace” și „probleme la inimă” ajung aproape unul de altul în spațiul vectorial, chiar dacă nu împart cuvinte. Acea apropiere face posibilă căutarea semantică.
Acest tutorial folosește modelul de embedding nomic-embed-text-v1, care este gratuit, open-source și rulează integral pe mașina ta locală. Modelul se descarcă automat de pe Hugging Face la prima rulare a scriptului și se salvează local pentru toate rulările viitoare. Modelul produce 768 de numere per intrare.
Indexuri pentru căutare vectorială
Un index de căutare vectorială îi spune lui MongoDB care câmp conține embeddingurile, câte dimensiuni să aștepte și ce funcție de similaritate să folosească pentru comparație. Trebuie să creezi acest index înainte de a putea rula orice interogare $vectorSearch.
Etapa de agregare $vectorSearch
$vectorSearch este etapa de agregare care rulează căutarea. Primește un vector de interogare, caută în câmpul indexat și returnează documente ordonate după similaritate semantică. O poți lega cu $project și alte etape, la fel ca orice alt pipeline de agregare.
Hai să începem.
Cerințe preliminare
Înainte să începi, asigură-te că ai următoarele:
- Python 3.8 sau mai nou instalat
- Un cont MongoDB Atlas cu un cluster M0 (nivel gratuit) configurat
pymongo4.7 sau mai nou, pachetele Pythonsentence-transformersșieinopsinstalate- Familiaritate de bază cu Python și colecțiile MongoDB
- Șirul tău de conexiune Atlas, disponibil în interfața Atlas la **Database > Connect > Drivers**
- Adresa ta IP este permisă în Atlas înainte să rulezi scripturile. Mergi la **Security > Network Access** în interfața Atlas și adaugă adresa ta IP curentă.
Configurează-ți proiectul
Creează un folder pentru proiect și navighează în el din terminal:
mkdir mongodb-semantic-search
cd mongodb-semantic-search
Creează și activează un mediu virtual pentru a izola dependințele proiectului:
python -m venv venv
source venv/bin/activate
Pe Windows, activează mediul virtual cu:
venv\Scripts\activate
Acum instalează pachetele necesare:
pip install pymongo sentence-transformers einops
Vei crea câte un fișier Python pentru fiecare pas al acestui tutorial. Toate fișierele merg în folderul mongodb-semantic-search.
Creează fișierul cu utilitare pentru embedding
Creează un fișier numit embedding_utils.py. Acest fișier conține ambele funcții de embedding folosite în acest tutorial:
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()
Avertisment: trust_remote_code=True permite modelului să execute în mașina ta cod Python specific modelului, descărcat de pe Hugging Face. Dacă sursa modelului pe Hugging Face este compromisă sau actualizată cu modificări malițioase, acel cod rulează automat în mediul tău. Fii precaut înainte de a folosi acest parametru într-un mediu de producție.
Generează și stochează embeddinguri vectoriale
Creează un fișier numit generate_embeddings.py. Acest fișier importă funcția de embedding din embedding_utils.py, generează un vector pentru câmpul de text al fiecărui document și stochează documentul complet, inclusiv embeddingul, în 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()
În codul de mai sus, collection.drop() șterge toate documentele și indexurile din colecție înainte de fiecare rulare. Rularea din nou a generate_embeddings.py fără această linie inserează documente duplicate, ceea ce face ca $vectorSearch să returneze același document de mai multe ori. Drop-ul colecției șterge și indexul de căutare vectorială, așa că trebuie să rulezi din nou create_vector_index.py ori de câte ori rulezi din nou generate_embeddings.py.
Rulează scriptul:
python generate_embeddings.py
Vei obține următoarea ieșire în terminal:
<All keys matched successfully>
Inserted 3 documents with embeddings.
Fiecare document din MongoDB conține acum atât textul original, cât și vectorul său cu 768 de dimensiuni în câmpul embedding. Vectorul trăiește alături de datele tale în același document, astfel încât nu este nevoie de stocare separată sau de căutări suplimentare la momentul interogării.
Creează un index MongoDB Vector Search
Creează un fișier numit create_vector_index.py. Acest fișier definește și creează un index de căutare vectorială pe câmpul embedding astfel încât MongoDB să poată rula interogări $vectorSearch asupra colecției tale.
Definiția indexului necesită trei câmpuri:
path: câmpul care conține embeddingurile (embeddingîn acest tutorial)numDimensions: trebuie să corespundă dimensiunii de ieșire a modelului (768pentrunomic-embed-text-v1)similarity: funcția de comparație (cosineeste corectă pentru acest model)
Valoarea numDimensions trebuie să se potrivească exact cu modelul tău de embedding. O neconcordanță duce la eșecul creării indexului:
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()
Rulează scriptul:
python create_vector_index.py
Vei obține următoarea ieșire în 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.
Indexul poate dura până la un minut să se construiască. Bucla de polling verifică la fiecare 5 secunde și iese doar când MongoDB confirmă că indexul este interogabil. Nu trece la pasul următor până nu vezi „vector_index is ready for querying.”
Convertește interogarea ta într-un vector
Creează un fișier numit generate_query_vector.py. Acest fișier importă funcția get_query_embedding() din fișierul embedding_utils.py. Rulează fișierul generate_query_vector.py pentru a verifica faptul că modelul de embedding se încarcă corect și produce un vector cu 768 de dimensiuni:
- Definește funcția
get_query_embedding()pe care pasul următor o importă. - Îl poți rula separat pentru a verifica dacă modelul funcționează corect.
Trebuie să folosești același model aici ca în primul pas. Un model diferit produce vectori care ocupă un spațiu numeric diferit, făcând comparația lipsită de sens:
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]}")
Rulează scriptul:
python generate_query_vector.py
Vei obține o ieșire similară cu aceasta:
<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]
În ieșirea de mai sus, Vector dimensions: 768 confirmă că ieșirea modelului se potrivește cu embeddingurile stocate.
Rulează o interogare $vectorSearch
Creează un fișier numit run_vector_search.py. Acest fișier importă funcția get_query_embedding() din fișierul embedding_utils.py și convertește termenul de căutare al unui utilizator într-un vector. run_vector_search.py rulează un pipeline de agregare $vectorSearch și afișează rezultatele ordonate după similaritate semantică:
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()
În codul de mai sus, doi parametri controlează comportamentul căutării:
numCandidatesstabilește dimensiunea setului inițial pe care MongoDB îl examinează înainte de a restrânge la rezultatele finale. O valoare mai mare îmbunătățește recall-ul, dar durează puțin mai mult.limitstabilește câte rezultate primești înapoi. În acest tutorial,limiteste 3, iar numCandidates este 150. Un punct de plecare comun este să setezinumCandidatesla 10–15 ori limita.
Acum rulează scriptul:
python run_vector_search.py
Vei obține o ieșire similară cu aceasta:
<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” are scorul cel mai mare, chiar dacă interogarea „I need an automated, scalable system for serious information storage” nu împărtășește cuvinte cu „MongoDB Atlas is a fully managed cloud database.” Căutarea îl returnează pentru că vectorii lor sunt apropiați ca sens: „...un sistem automatizat, scalabil pentru stocarea informațiilor” se mapează semantic la „...o bază de date cloud complet administrată”. O căutare text pentru aceeași interogare ar returna zero rezultate, deoarece niciunul dintre cuvintele exacte din interogare nu apare în vreun document. Așa funcționează căutarea semantică, conform așteptărilor.
Idei principale
- MongoDB Vector Search rulează pe un cluster M0 Free tier. Nu este necesar un plan plătit.
- Trebuie să folosești același model de embedding atât pentru generarea embeddingurilor stocate, cât și pentru generarea vectorilor de interogare. Amestecarea modelelor produce rezultate lipsite de sens.
- Valoarea
numDimensionsdin definiția indexului trebuie să se potrivească exact cu dimensiunea de ieșire a modelului tău de embedding.nomic-embed-text-v1produce întotdeauna 768 de dimensiuni. input_type="document"optimizează embeddingurile pentru stocare.input_type="query"le optimizează pentru regăsire. Folosește tipul corect la fiecare etapă.numCandidatescontrolează dimensiunea plasei de căutare pe care MongoDB o aruncă înainte de a restrânge la rezultatele finalelimit. O valoare mai mare îmbunătățește recall-ul cu prețul timpului de interogare.vectorSearchScoreordonează rezultatele după similaritate semantică. Rezultatele nu trebuie să conțină vreunul dintre cuvintele exacte din interogare. Intervalele de scor variază în funcție de model și setul de date, dar următoarele praguri sunt un punct de plecare util pentrunomic-embed-text-v1cu similaritate cosine:- 0,9 și peste: Sens aproape identic. Documentul și interogarea sunt aproape aceleași semantic.
- 0,7 până la 0,9: Relevanță puternică. Documentul se leagă clar de intenția interogării.
- 0,5 până la 0,7: Relevanță moderată. Documentul este legat tematic, dar folosește un alt cadru sau context.
- Sub 0,5: Relevanță slabă. Legătura e lejeră și rezultatul poate să nu fie util.
Poți găsi toate mostrele de cod pentru acest tutorial în repository-ul GitHub.
Resurse suplimentare
- Prezentare generală MongoDB Vector Search acoperă capabilitățile complete ale MongoDB Vector Search, inclusiv filtrarea și cuantizarea.
- Cum să faci căutare hibridă îți arată cum să combini căutarea vectorială și căutarea full-text într-o singură interogare.
- Cum să creezi embeddinguri vectoriale explică cum să generezi embeddinguri vectoriale pentru date text din colecțiile tale folosind modele de la Voyage AI, OpenAI și alți furnizori open-source.
- Retrieval-Augmented Generation (RAG) cu MongoDB îți arată cum să folosești căutarea semantică drept strat de regăsire într-o aplicație de tip retrieval-augmented generation.
Întrebări frecvente
Am nevoie de un plan Atlas plătit ca să implementez căutarea semantică?
Nu. Toți cei patru pași din acest tutorial rulează pe un cluster M0 Free tier, care este gratuit.
Ce se întâmplă dacă folosesc un alt model de embedding pentru interogare decât pentru documentele stocate?
Rezultatele vor fi lipsite de sens. Vectorii nu vor fi comparabili pentru că modelele diferite mapază textul în spații numerice diferite. Folosește întotdeauna același model atât pentru indexare, cât și pentru interogare.
Care este diferența dintre `numCandidates` și `limit`?
numCandidates reprezintă câți vectori examinează MongoDB în timpul căutării. limit reprezintă câte dintre rezultatele de top îți returnează. O valoare mai mare pentru numCandidates îmbunătățește calitatea rezultatelor cu prețul unor interogări ușor mai lente. Un punct de plecare comun este să setezi numCandidates la 10–15 ori limit.
Pot folosi împreună căutarea semantică și căutarea text?
Da. MongoDB suportă căutarea hibridă, care combină $vectorSearch și $search într-un singur pipeline.
Funcționează căutarea semantică pentru alte limbi decât engleza?
Depinde de modelul tău de embedding. nomic-embed-text-v1 este antrenat în principal pe text în engleză. Pentru cazuri multilingve, alege un model multilingv antrenat pe limbile pe care le conțin datele tale.