PostgreSQL JSONB und Volltextsuche in Django

  • 11 Jan 2026
  • CODLAB Team
  • 3 min
  • 383

PostgreSQL: Über die Relationale Datenbank Hinaus

PostgreSQL bietet erweiterte Funktionen, die es perfekt für moderne Anwendungen machen: semi-strukturierte Daten mit JSONB und integrierte Textsuche.

1. JSONB in Django

Modell mit JSONField

from django.db import models
from django.contrib.postgres.fields import ArrayField

class Product(models.Model):
    name = models.CharField(max_length=200)
    price = models.DecimalField(max_digits=10, decimal_places=2)

    # JSONB für flexible Daten
    metadata = models.JSONField(default=dict)
    specifications = models.JSONField(default=dict)

    # String-Array
    tags = ArrayField(
        models.CharField(max_length=50),
        default=list,
        blank=True
    )

    class Meta:
        indexes = [
            # GIN-Index für effiziente JSONB-Abfragen
            models.Index(
                name='idx_product_metadata_gin',
                fields=['metadata'],
                opclasses=['jsonb_path_ops']
            ),
        ]

JSONB-Abfragen

from django.db.models import F
from django.contrib.postgres.fields.jsonb import KeyTextTransform

# Zugriff auf bestimmte Schlüssel
products = Product.objects.filter(
    metadata__brand='Apple'
)

# Verschachtelte Schlüssel
products = Product.objects.filter(
    specifications__display__size__gte=6.0
)

# Schlüsselexistenz prüfen
products = Product.objects.filter(
    metadata__has_key='warranty'
)

# Enthält Wert
products = Product.objects.filter(
    metadata__contains={'featured': True}
)

# Enthalten in
products = Product.objects.filter(
    metadata__contained_by={'brand': 'Apple', 'category': 'phone'}
)

# Annotation mit JSON-Wert
from django.db.models.functions import Cast
from django.db.models import FloatField

products = Product.objects.annotate(
    rating=Cast(
        KeyTextTransform('rating', 'metadata'),
        FloatField()
    )
).filter(rating__gte=4.5)

Atomare JSONB-Updates

from django.db.models import F
from django.db.models.functions import JSONObject
from django.contrib.postgres.fields.jsonb import KeyTransform

# Bestimmten Schlüssel aktualisieren (Django 4.2+)
Product.objects.filter(pk=1).update(
    metadata=F('metadata') | {'updated_at': '2024-01-15'}
)

# Schlüssel entfernen
from django.contrib.postgres.expressions import ArraySubquery
Product.objects.filter(pk=1).update(
    metadata=models.Func(
        F('metadata'),
        models.Value('deprecated_key'),
        function='jsonb_delete_path'
    )
)

# Numerischen Wert in JSON inkrementieren
from django.db.models.expressions import RawSQL
Product.objects.filter(pk=1).update(
    metadata=RawSQL(
        "jsonb_set(metadata, '{views}', (COALESCE(metadata->>'views', '0')::int + 1)::text::jsonb)",
        []
    )
)

2. Volltextsuche

Modell-Setup

from django.contrib.postgres.search import (
    SearchVector, SearchQuery, SearchRank,
    SearchVectorField, TrigramSimilarity
)
from django.contrib.postgres.indexes import GinIndex

class Article(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    excerpt = models.TextField(blank=True)

    # Feld für indizierte Suche
    search_vector = SearchVectorField(null=True)

    class Meta:
        indexes = [
            GinIndex(fields=['search_vector']),
        ]

    def save(self, *args, **kwargs):
        super().save(*args, **kwargs)
        # Search-Vektor aktualisieren
        Article.objects.filter(pk=self.pk).update(
            search_vector=(
                SearchVector('title', weight='A', config='german') +
                SearchVector('excerpt', weight='B', config='german') +
                SearchVector('content', weight='C', config='german')
            )
        )

Volltext-Abfragen

# Einfache Suche
from django.contrib.postgres.search import SearchVector, SearchQuery

articles = Article.objects.annotate(
    search=SearchVector('title', 'content', config='german')
).filter(
    search=SearchQuery('django python', config='german')
)

# Mit Ranking
from django.contrib.postgres.search import SearchRank

query = SearchQuery('machine learning', config='german')
articles = Article.objects.annotate(
    rank=SearchRank(F('search_vector'), query)
).filter(
    search_vector=query
).order_by('-rank')

# Headline (Treffer hervorheben)
from django.contrib.postgres.search import SearchHeadline

articles = Article.objects.annotate(
    headline=SearchHeadline(
        'content',
        query,
        start_sel='',
        stop_sel='',
        config='german'
    )
)

# Erweiterte Abfragen
# UND
query = SearchQuery('django') & SearchQuery('api')

# ODER
query = SearchQuery('django') | SearchQuery('flask')

# NICHT
query = SearchQuery('python') & ~SearchQuery('java')

# Phrasensuche
query = SearchQuery('machine learning', search_type='phrase')

Trigram-Suche (Fuzzy)

# Erweiterung installieren
# CREATE EXTENSION pg_trgm;

from django.contrib.postgres.search import TrigramSimilarity, TrigramDistance

# Ähnliche Artikel finden (typo-tolerant)
articles = Article.objects.annotate(
    similarity=TrigramSimilarity('title', 'djnago')  # absichtlicher Typo
).filter(
    similarity__gt=0.3
).order_by('-similarity')

# Trigram-Distanz
articles = Article.objects.annotate(
    distance=TrigramDistance('title', 'machine lerning')
).filter(
    distance__lt=0.7
).order_by('distance')

# Kombination von Volltext und Trigram
from django.db.models import Q, Value
from django.db.models.functions import Greatest

search_term = 'machin lernin'  # Mit Fehlern

articles = Article.objects.annotate(
    similarity=Greatest(
        TrigramSimilarity('title', search_term),
        TrigramSimilarity('content', search_term)
    ),
    rank=SearchRank(
        F('search_vector'),
        SearchQuery(search_term, config='german')
    )
).filter(
    Q(similarity__gt=0.2) | Q(rank__gt=0.1)
).order_by('-similarity', '-rank')

3. Optimierte Indizes

class Article(models.Model):
    # ... fields ...

    class Meta:
        indexes = [
            # GIN für Volltext
            GinIndex(
                fields=['search_vector'],
                name='article_search_gin'
            ),

            # GIN für JSONB
            GinIndex(
                fields=['metadata'],
                name='article_metadata_gin',
                opclasses=['jsonb_path_ops']
            ),

            # Partieller Index
            models.Index(
                fields=['published_at'],
                name='article_published_idx',
                condition=models.Q(status='published')
            ),

            # Trigram-Index
            GinIndex(
                fields=['title'],
                name='article_title_trgm',
                opclasses=['gin_trgm_ops']
            ),
        ]

4. Management-Command für Search-Rebuild

# management/commands/rebuild_search.py
from django.core.management.base import BaseCommand
from django.contrib.postgres.search import SearchVector
from blog.models import Article

class Command(BaseCommand):
    help = 'Search-Vektoren für alle Artikel neu erstellen'

    def handle(self, *args, **options):
        self.stdout.write('Erstelle Search-Vektoren neu...')

        Article.objects.update(
            search_vector=(
                SearchVector('title', weight='A', config='german') +
                SearchVector('excerpt', weight='B', config='german') +
                SearchVector('content', weight='C', config='german')
            )
        )

        count = Article.objects.count()
        self.stdout.write(
            self.style.SUCCESS(f'{count} Artikel aktualisiert')
        )

Best Practices

  • Verwenden Sie JSONB statt JSON für effiziente Abfragen
  • Erstellen Sie GIN-Indizes für häufig abgefragte JSONB-Felder
  • Verwenden Sie SearchVectorField für häufige Suchen
  • Kombinieren Sie Volltext und Trigram für Fuzzy-Suchen
  • Aktualisieren Sie Search-Vektoren mit Triggern oder Signals

Fazit

PostgreSQL mit JSONB und Volltextsuche macht Django zu einer leistungsstarken Plattform für Anwendungen mit erweiterten Such- und Datenflexibilitätsanforderungen.

Teilen Sie diesen Artikel

Kunden

Noch keine Kommentare. Seien Sie der Erste!

Einen Kommentar hinterlassen

Wird nicht veröffentlicht

Verwandte Artikel

Projekt im Kopf?

Wir entwickeln maßgeschneiderte Software und KI-Lösungen. Lassen Sie uns gemeinsam Ihr nächstes Projekt starten.

Kostenloses Erstgespräch