Weiter zum Inhalt
MongoDB

So durchsuchst du Bilder und Texte mit MongoDB Vector Search und FastAPI

Baue eine End-to-End-Vectorsuche mit FastAPI und MongoDB Atlas. Lerne in diesem Praxis-Tutorial, Text- und Bild-Embeddings zu erzeugen, zu speichern und abzufragen.
Moses Anumadu's photo
Moses Anumadu
Aktualisiert · 10 Min. lesen

Entdecke KI

ChatGPTClaudePerplexity

Erstelle dein kostenloses Konto

Üben und Lernpfad deinen Fortschritt.

Weiter Mit Google
Durch Klick auf die Schaltfläche akzeptierst du unsere Nutzungsbedingungen, unsere Datenschutzrichtlinie und die Speicherung deiner Daten in den USA.

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

all-MiniLM-L6-v2

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:

  1. Erstelle eine Datenbank. Wir nennen sie vector_db. Mehr dazu in der Dokumentation.
  2. Lege darin zwei Collections an:
    • texts: für Textdokumente und ihre Embeddings
    • images: 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.

MongoDB Atlas dashboard explaining the process to create a search index

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

MongoDB Atlas dashboard explaining the process to create a search index. Step one of the process.

MongoDB Atlas dashboard explaining the process to create a search index. Step two of the process.

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.MongoDB Atlas dashboard explaining the process to create a search index. Step three of the process.

Image contains a JSON text editor for the JSON definition of the text search.

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:

Image of a terminal with Ngrok server running. 
Contains the localhost URL and the tunneled web URL

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

Image of an API call made with Postman, Ngrok URL and response from the API call.

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:

  1. Wir wandeln die Nutzeranfrage in ein Embedding um.
  2. MongoDB liefert die nächsten Nachbarn.
  3. 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:

Image of an API call made with Postman, Ngrok URL and response from the API call.

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.

Image of an API call made with Postman, Ngrok URL and response from the API call.

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.

Image of an API call made with Postman, Ngrok URL to /search-image route and response from the API call.

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.


Moses Anumadu's photo
Author
Moses Anumadu

Softwareentwickler

Themen
MongoDB

Top-DataCamp-Kurse

Kurs

Einführung in MongoDB mit Python

3 Std.
24.4K
Lerne, wie du flexibel strukturierte Daten mit MongoDB bearbeiten und analysieren kannst.
Details anzeigenKurs Starten

Erstelle dein kostenloses Konto

Üben und Lernpfad deinen Fortschritt.

Weiter Mit Google
Durch Klick auf die Schaltfläche akzeptierst du unsere Nutzungsbedingungen, unsere Datenschutzrichtlinie und die Speicherung deiner Daten in den USA.
Mehr anzeigen
Ähnlich

Tutorial

Ein Leitfaden zu Python-Hashmaps

Finde heraus, was Hashmaps sind und wie sie in Python mit Hilfe von Wörterbüchern umgesetzt werden.
Javier Canales Luna's photo

Javier Canales Luna

11 Min.

Tutorial

Python Datenstrukturen Tutorial

Mach dich mit Python-Datenstrukturen vertraut: Lerne mehr über Datentypen und primitive sowie nicht-primitive Datenstrukturen wie Strings, Listen, Stapel usw.
Sejal Jaiswal's photo

Sejal Jaiswal

24 Min.

Tutorial

Python-Arrays

Python-Arrays mit Code-Beispielen. Lerne noch heute, wie du mit Python NumPy Arrays erstellen und ausdrucken kannst!
DataCamp Team's photo

DataCamp Team

3 Min.

Tutorial

Python-Tutorial zum Verknüpfen von Zeichenfolgen

Lerne verschiedene Methoden zum Verknüpfen von Zeichenfolgen in Python kennen, mit Beispielen, die jede Technik zeigen.
DataCamp Team's photo

DataCamp Team

5 Min.

Tutorial

Python JSON-Daten: Ein Leitfaden mit Beispielen

Lerne, wie man mit JSON in Python arbeitet, einschließlich Serialisierung, Deserialisierung, Formatierung, Leistungsoptimierung, Umgang mit APIs und Verständnis der Einschränkungen und Alternativen von JSON.
Moez Ali's photo

Moez Ali

6 Min.

Tutorial

Fibonacci-Folge in Python: Lerne und entdecke Programmiertechniken

Finde raus, wie die Fibonacci-Folge funktioniert. Schau dir die mathematischen Eigenschaften und die Anwendungen in der echten Welt an.
Laiba Siddiqui's photo

Laiba Siddiqui

6 Min.

Mehr anzeigen