Kurs
Schlagwortsuche funktioniert gut für einfache Abfragen, stößt aber schnell an Grenzen, sobald die Bedeutung wichtiger ist als die exakte Wortwahl. Wenn ein Dokument von “kürzeren Reaktionszeiten” spricht und ein anderes von “besserer Performance”, behandelt eine klassische Suche sie als verschieden, obwohl sie dasselbe meinen. Die semantischen Beziehungen gehen verloren, nur weil die Formulierungen abweichen.
Vectorsuche löst dieses Problem, indem Texte und Bilder als Embeddings dargestellt werden: numerische Vektoren aus ML-Modellen, die Kontext, Intention und semantische Ähnlichkeit erfassen. Speichern wir diese Embeddings in MongoDB, können wir danach suchen und tatsächlich verwandte Inhalte finden – unabhängig von der exakten Wortwahl.
In diesem Tutorial bauen wir ein praxisnahes End-to-End-Beispiel mit FastAPI und MongoDB Atlas Vector Search. Wir erzeugen Text-Embeddings mit SentenceTransformers und Bild-Embeddings mit einem CLIP-basierten Modell, speichern beides in MongoDB und führen Ähnlichkeitssuchen darüber aus. Am Ende steht ein funktionierendes Fundament für semantische Artikelsuche, Produktempfehlungen, visuelle Ähnlichkeitssuche und viele weitere reale Anwendungsfälle.
Also los geht’s.
Voraussetzungen
Bevor wir loslegen, brauchen wir ein paar Dinge. Du solltest Grundkenntnisse in Python, FastAPI und MongoDB mitbringen. Wir nutzen nichts Fortgeschrittenes – alles Wichtige erklären wir unterwegs.
Das benötigen wir:
- Python 3.10+
- Ein MongoDB Atlas-Cluster mit aktivierter Vector Search
- FastAPI-Umgebung
- Postman oder cURL
- Internetzugang zum Laden der Embedding-Modelle
Python-Bibliotheken, die wir nutzen
Diese installieren wir im Setup:
- FastAPI: unser Webframework
- uvicorn: Entwicklungsserver
- pymongo: MongoDB-Treiber
- sentence-transformers: Text-Embedding-Modell
- open_clip: CLIP-basiertes Bild-Embedding-Modell
- torch: Voraussetzung für CLIP und SentenceTransformers
- Pillow: Bildverarbeitung (PIL)
Damit haben wir alles, um Embeddings zu erzeugen, in MongoDB zu speichern und über Vectorsuche effizient abzufragen.
Vectorsuche verstehen
Bevor wir Code schreiben, hilft ein kurzer Blick unter die Haube des Workflows. Vectorsuche ist nicht kompliziert, wenn man sie herunterbricht. Mit den Grundlagen im Kopf fällt der Rest deutlich leichter.
Was ist ein Embedding?
Ein Embedding ist einfach eine Liste von Zahlen (ein Vektor), die die Bedeutung eines Datums repräsentiert.
Bei Text erfasst das Embedding etwa Kontext und Intention. Bei Bildern sind es visuelle Muster und Merkmale.
Ein stark vereinfachtes Beispiel könnte so aussehen:
[0.21, -0.17, 0.89, ...]
Zwei semantisch ähnliche Dinge – etwa zwei Sätze mit derselben Aussage oder zwei Bilder mit ähnlichen Objekten – landen bei Vektoren, die sich mathematisch nahe sind. Vectorsuche misst genau diese Nähe.
Wie MongoDB Embeddings nutzt
MongoDB erzeugt Embeddings nicht selbst. Stattdessen konzentriert es sich auf seine Stärken:
- Speichern von Embedding-Vektoren
- Indexierung über einen Vector Index
- Schnelles Auffinden der nächsten Nachbarn
Kurz: Unsere Modelle erzeugen die Embeddings, MongoDB übernimmt die Suche.
Diese klare Trennung macht das System flexibel und skalierbar.
Arten von Embedding-Modellen
Wir können jedes Embedding-Modell verwenden, solange es einen numerischen Vektor ausgibt. Unterschiedliche Modelle sind auf unterschiedliche Datentypen spezialisiert:
|
Datentyp |
Beispielmodell |
Hinweise |
|
Text |
|
Leichtgewichtig und weit verbreitet für NLP |
|
Bild |
CLIP/OpenCLIP |
Wandelt Bilder in semantische Vektoren um |
|
Multimodal |
Multimodal |
Verarbeitet Text und Bilder gemeinsam |
Diesen Workflow setzen wir mit FastAPI und MongoDB Atlas um.
Danach können wir Texte und Bilder nach Bedeutung durchsuchen – etwas, das klassische Schlagwortsuche nicht leisten kann.
MongoDB Atlas einrichten
Bevor wir die FastAPI-Anwendung bauen, brauchen wir einen Ort zum Speichern und Abfragen unserer Embeddings. MongoDB Atlas macht das einfach, und Vector Indexes sind in wenigen Minuten angelegt.
1. Cluster erstellen
Falls noch nicht vorhanden, erstelle in MongoDB Atlas ein neues Cluster. Für dieses Tutorial reicht die Free-Tier (M0). Details findest du in der MongoDB-Dokumentation zu Cluster erstellen.
2. Datenbank und Collections anlegen
Im Cluster:
- Erstelle eine Datenbank. Wir nennen sie
vector_db. Mehr dazu in der Dokumentation. - Lege darin zwei Collections an:
texts: für Textdokumente und ihre Embeddingsimages: für Bilddateinamen und Bild-Embeddings
Mehr Details zur Collection-Erstellung findest du in der Doku.
Wir trennen Bild- und Text-Collection, da sie auf unterschiedlichen Embedding-Modellen und Vektordimensionen basieren.
3. Vector Indexes erstellen
Für schnelle Ähnlichkeitssuchen benötigt MongoDB einen Vector Index. Wir legen je einen Index für Text- und Bild-Embeddings an.
Du erstellst sie in Atlas → Collections → Search Indexes. Fangen wir mit der Text-Collection an.
Die folgende Abbildung zeigt den Ablauf.

Klicke im nächsten Schritt auf Vector Search -> JSON Editor -> Next. Die folgenden Bilder helfen dir beim Verständnis.


Vector Index für Text
Im Vector-Search-Index passt du den JSON-Text an unseren Code an.
Wir verwenden das Modell all-MiniLM-L6-v2 für Text, das 384-dimensionale Embeddings erzeugt.
Hier ist die JSON-Definition:
{
"fields": [
{
"type": "vector",
"path": "embedding",
"numDimensions": 384,
"similarity": "cosine"
}
]
}
Gib dem Index den Namen: vector_texts_search und speichere.
Damit kann MongoDB Kosinus-Ähnlichkeit über Text-Embeddings berechnen. Das folgende Bild zeigt den Schritt noch einmal im Detail.
Vector Index für Bilder
Für Bilder nutzen wir ein CLIP-basiertes Modell, das 512-dimensionale Vektoren erzeugt. Der Bildindex muss diese Dimension abbilden. Die JSON-Definition entspricht dem Code unten. Abgesehen davon ist der Ablauf identisch.
{
"fields": [
{
"type": "vector",
"path": "embedding",
"numDimensions": 512,
"similarity": "cosine"
}
]
}
Nenne diesen Index: vector_images_search.
Sobald beide Indizes aktiv sind, ist MongoDB Atlas bereit für Vector-Search-Abfragen.
Das FastAPI-Projekt einrichten
Nachdem Cluster und Vector Indexes bereit sind, richten wir die FastAPI-App ein, die Embeddings erzeugt und in MongoDB speichert. Wir halten die Struktur schlank, da es hier um den Workflow geht – nicht um einen vollständigen Produktionsdienst.
1. Benötigte Abhängigkeiten installieren
Wir installieren FastAPI, Uvicorn, den MongoDB-Python-Treiber und die Bibliotheken für Text- und Bild-Embeddings. Führe aus:
pip install fastapi uvicorn pymongo python-dotenv sentence-transformers torch pillow open_clip_torch python-multipart
Kurz, wofür wir sie brauchen:
- fastapi — unser API-Framework
- uvicorn — ASGI-Server für FastAPI
- pymongo — MongoDB-Treiber
- python-dotenv — lädt Umgebungsvariablen aus
.env - sentence-transformers — Text-Embedding-Modell
- torch — benötigt von Embedding-Modellen
- pillow — grundlegende Bildverarbeitung
- open_clip_torch — CLIP-basiertes Bild-Embedding-Modell
- python-multipart — erforderlich für Datei-Uploads via FastAPI
Sind die Pakete installiert, strukturieren wir das Projekt.
2. Projektstruktur
Wir behalten alles in einer Python-Datei, die Konfiguration läuft über Umgebungsvariablen:
fastapi-mongodb-vector-search/
│── main.py
│── .env
│── venv/
main.py enthält die gesamte Anwendung: Text-Endpunkte, Bild-Endpunkte und die MongoDB-Verbindung.
MONGODB_URI="your connection string"
DB_NAME="vector_db"
TEXT_COLLECTION="texts"
IMAGE_COLLECTION="images"
- MONGODB_URI — die Atlas-URI deines Clusters
- DB_NAME — die erstellte Datenbank
- TEXT_COLLECTION — Speicherort für Text + Embeddings
- IMAGE_COLLECTION — Speicherort für Bild-Embeddings
FastAPI lädt diese Werte beim Start und trennt damit Konfiguration vom Code.
Textsuche implementieren
Mit dem eingerichteten Projekt bauen wir zuerst die Textsuche. Hier steckt die Kernidee der Vectorsuche: Wir erzeugen Embeddings für unsere Texte, speichern sie in MongoDB und fragen sie über den zuvor angelegten Vector Index ab.
Wir gehen in zwei kleinen Schritten vor, damit sich alles einfach testen und debuggen lässt.
a. Text hinzufügen und Embeddings erzeugen
Der erste Endpunkt nimmt einen Titel und Inhalt entgegen. Wir kombinieren beides zu einem String, erzeugen mit SentenceTransformers ein Embedding und speichern das Dokument zusammen mit dem Vektor.
So sieht das im Code aus:
# main.py (excerpt)
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from pymongo import MongoClient
from sentence_transformers import SentenceTransformer
from dotenv import load_dotenv
import os
load_dotenv()
app = FastAPI()
# --- DB setup ---
client = MongoClient(os.getenv("MONGODB_URI"))
db = client[os.getenv("DB_NAME")]
texts_coll = db[os.getenv("TEXT_COLLECTION")]
Images_coll = db[os.getenv("IMAGE_COLLECTION")]
# --- text embedding model ---
MODEL_NAME = "all-MiniLM-L6-v2"
text_model = SentenceTransformer(MODEL_NAME)
# --- request schema ---
class AddTextRequest(BaseModel):
title: str
content: str
@app.post("/add-text")
def add_text(payload: AddTextRequest):
try:
# Combine fields for richer embeddings
text = f"{payload.title}\n\n{payload.content}"
# Generate text embedding
embedding = text_model.encode(text).tolist()
# Store document in MongoDB
doc = {
"title": payload.title,
"content": payload.content,
"embedding": embedding,
"model": MODEL_NAME,
}
result = texts_coll.insert_one(doc)
return {
"inserted_id": str(result.inserted_id),
"title": payload.title
}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
So testest du diesen Endpunkt
Zum Testen eignen sich cURL, Postman und ähnliche Tools. In diesem Tutorial nutzen wir Postman.
Wir müssen unseren FastAPI-Endpunkt außerdem ins Internet exponieren, um ihn im Web-Postman zu testen. Dafür verwenden wir Ngrok. Wenn du neu bei Ngrok bist, schau in die Dokumentation. Zum Testen starte zuerst FastAPI mit folgendem Befehl:
uvicorn main:app --reload
Leite dann dein localhost mit Ngrok ins Internet weiter:
Ngrok http 8000
Ngrok erzeugt eine URL zum Testen, wie im Bild unten:

Sende mit Postman eine POST-Anfrage an den add-text-Endpunkt. Das folgende Bild zeigt Request und erwartete Response.

Erwartete Antwort:
{
"inserted_id": "65f...",
"title": "Why FastAPI is great."
}
Damit ist klar: Das Embedding-Modell wurde korrekt geladen und MongoDB hat den Vektor gespeichert. Die Text-Pipeline läuft.
b. Text per Vectorsuche finden
Jetzt, wo wir Texte hinzufügen können, suchen wir sie mit dem $vectorSearch-Stage von MongoDB. Konkret:
- Wir wandeln die Nutzeranfrage in ein Embedding um.
- MongoDB liefert die nächsten Nachbarn.
- Wir geben die ähnlichsten Dokumente zurück.
Füge den folgenden Endpunkt zu main.py hinzu:
class SearchTextRequest(BaseModel):
query: str
limit: int = 5
@app.post("/search-text")
def search_text(payload: SearchTextRequest):
try:
# Convert search query to embedding
query_vector = text_model.encode(payload.query).tolist()
# Vector search pipeline
pipeline = [
{
"$vectorSearch": {
"queryVector": query_vector,
"path": "embedding",
"numCandidates": 100,
"limit": payload.limit,
"index": "vector_texts_search" # field MUST match the index name in Atlas exactly
}
},
{
"$project": {
"title": 1,
"content": 1,
"score": {"$meta": "vectorSearchScore"},
"_id": 0
}
}
]
results = list(texts_coll.aggregate(pipeline))
return {"results": results}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
> Der Parameter numCandidates legt fest, wie viele potenzielle Treffer bewertet werden, bevor die Top-Ergebnisse zurückkommen.
> Höhere Werte verbessern die Genauigkeit, erhöhen aber die Abfragezeit. Ein guter Richtwert ist das 10- bis 20-Fache deines Limits.
Textsuche testen
Nachdem du ein oder mehrere Dokumente hinzugefügt hast, sende eine POST-Anfrage an /search-text über Postman mit dem Ngrok-Link, wie im Bild unten:

Erwartete Ausgabe:
{
"results": [
{
"title": "Why FastAPI is great",
"content": "FastAPI makes building APIs simple and fast.",
"score": 0.85
}
]
}
Wenn du ein Ergebnis mit Ähnlichkeitsscore siehst, funktioniert deine Text-Vectorsuche End-to-End. Läuft dieser Teil stabil, geht es weiter mit Bildern.
Bildsuche implementieren
Läuft die Text-Pipeline, übertragen wir das Prinzip auf Bilder. Der Ablauf ist ähnlich – der wichtigste Unterschied ist die Embedding-Erzeugung. Statt SentenceTransformers nutzen wir ein CLIP-basiertes Modell, das Bilder in 512-dimensionale Vektoren verwandelt.
Auch hier gehen wir schrittweise vor, damit Tests und Debugging leichtfallen.
a. Bild hinzufügen und Embeddings erzeugen
Dieser Endpunkt nimmt eine Bilddatei entgegen, konvertiert sie in einen Tensor für CLIP, erzeugt ein 512-dimensionales Embedding und speichert es in MongoDB.
So sieht das aus:
# --- CLIP model setup ---
import torch
import open_clip
from PIL import Image
import io
from fastapi import UploadFile, File
# Load CLIP model once when the app starts
clip_model, _, preprocess = open_clip.create_model_and_transforms(
"ViT-B-32",
pretrained="openai"
)
device = "cuda" if torch.cuda.is_available() else "cpu"
clip_model.to(device)
@app.post("/add-image")
async def add_image(file: UploadFile = File(...)):
"""
Upload an image, generate its CLIP embedding, and store in MongoDB.
"""
try:
# Read file contents
image_bytes = await file.read()
image = Image.open(io.BytesIO(image_bytes)).convert("RGB")
# Preprocess for CLIP and encode
image_tensor = preprocess(image).unsqueeze(0).to(device)
with torch.no_grad():
image_embedding = clip_model.encode_image(image_tensor)
# Normalize the embedding (important for cosine similarity)
image_embedding /= image_embedding.norm(dim=-1, keepdim=True)
vector = image_embedding.cpu().numpy().flatten().tolist()
# Store in MongoDB
doc = {
"filename": file.filename,
"embedding": vector
}
result = images_coll.insert_one(doc)
return {
"inserted_id": str(result.inserted_id),
"filename": file.filename
}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
Warum Normalisierung wichtig ist
CLIP-Embeddings funktionieren am besten mit Kosinus-Ähnlichkeit. Damit Vergleiche präzise und stabil sind, normalisieren wir die Vektoren:
image_embedding /= image_embedding.norm(dim=-1, keepdim=True)
So spiegelt die Kosinus-Distanz die tatsächliche Ähnlichkeit wider.
Bild-Upload-Endpunkt testen
Teste in Postman mit einer POST-Anfrage an /add-image. Sende im Body Form-Data mit einem .jpg- oder .png-Bild. Das folgende Bild zeigt den Ablauf.

Wenn alles passt, bekommst du etwa:
{
"inserted_id": "692454f6156fbb762ed865a0",
"filename": "file-name.png"
}
Damit sind deine Bild-Embeddings erfolgreich in MongoDB gespeichert.
b. Bilder per Vectorsuche finden
Nun bauen wir den Endpunkt, der ein hochgeladenes Query-Bild in ein Embedding umwandelt und über unseren Bild-Vector-Index die nächsten Treffer findet.
Füge Folgendes in main.py ein:
@app.post("/search-image")
async def search_image(file: UploadFile = File(...)):
"""
Upload an image and find the most visually similar images in MongoDB.
"""
try:
# Read the uploaded file
image_bytes = await file.read()
image = Image.open(io.BytesIO(image_bytes)).convert("RGB")
# Generate embedding for the query image
image_tensor = preprocess(image).unsqueeze(0).to(device)
with torch.no_grad():
query_embedding = clip_model.encode_image(image_tensor)
query_embedding /= query_embedding.norm(dim=-1, keepdim=True)
query_vector = query_embedding.cpu().numpy().flatten().tolist()
# Vector search pipeline
pipeline = [
{
"$vectorSearch": {
"queryVector": query_vector,
"path": "embedding",
"numCandidates": 100,
"limit": 5,
"index": "vector_images_search"
}
},
{
"$project": {
"filename": 1,
"score": {"$meta": "vectorSearchScore"},
"_id": 0
}
}
]
results = list(images_coll.aggregate(pipeline))
return {"results": results}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
Bildsuche testen
Teste in Postman mit einer POST-Anfrage an /search-image. Sende im Body Form-Data mit einem Bild, das den zuvor gespeicherten ähnelt. Das folgende Bild zeigt den Ablauf.

Die erwartete Ausgabe sieht so aus:
{
"results": [
{
"filename": "image1.png",
"score": 1.0
},
{
"filename": "image2.png",
"score": 0.81
}
]
}
Ein Score von 1.0 bedeutet, dass das identische Bild gefunden wurde. Werte nahe daran (0,7–0,9) deuten auf hohe visuelle Ähnlichkeit hin.
Wenn du eine Ergebnisliste siehst, funktioniert deine Bild-Vectorsuche.
Wenn alles läuft, hast du jetzt ein vollständiges semantisches Suchsystem für Text und Bild.
Fazit
In diesem Tutorial haben wir einen vollständigen semantischen Such-Workflow mit FastAPI und MongoDB Atlas aufgebaut. Wir haben Text-Embeddings mit SentenceTransformers und Bild-Embeddings mit einem CLIP-Modell erzeugt, alles in MongoDB gespeichert und mit MongoDB Vector Search sowie Postman abgefragt. Dieses Muster bildet die Basis für semantische Dokumentsuche, Bildähnlichkeit, Empfehlungssysteme und multimodale KI-Features.
Die Stärke dieser Architektur liegt in ihrer Flexibilität: MongoDB schreibt dir keine Modelle vor. Solange Embeddings numerische Vektoren sind und die Dimensionen zum Index passen, kannst du jedes Text-, Bild- oder Multimodal-Embedding verwenden, das dein Use Case erfordert.
Auf dieser Basis kannst du weiter ausbauen – Metadaten speichern, Ranking-Logik entwickeln oder in Nutzeranwendungen integrieren. Mit den Managed Services von MongoDB Atlas skaliert derselbe Workflow zudem problemlos.
Das vollständige Projekt findest du auf GitHub.

Softwareentwickler

