gateway/docs/real-estate-feature-integration-guide/09-database-schema.md

8.6 KiB

Datenbank-Schema

← Zurück: Feature Lifecycle | Weiter: Sicherheitshinweise →

Die Datenbank-Tabellen werden automatisch vom DatabaseConnector erstellt, basierend auf den Pydantic-Modellen:

Chat-Interface Tabellen:

  • RealEstateQuery: Speichert Abfragen
  • RealEstateQueryResult: Speichert Abfrageergebnisse (mit JSONB für rowData)
  • RealEstateChatSession: Speichert Chat-Sessions

Real Estate-Datenmodell Tabellen:

Die folgenden Tabellen werden basierend auf den Real Estate-Datenmodell-Entitäten erstellt:

  • Projekt: Bauprojekte (mit parzellen, dokumente, kontextInformationen als JSONB)
  • Parzelle: Grundstücke mit Bauparametern (mit parzellenNachbarschaft, dokumente, kontextInformationen als JSONB)
  • Dokument: Dateien und URLs
  • Kontext: Zusatzinformationen
  • GeoPolylinie: Geometrische Linien/Polygone (mit punkte als JSONB)
  • GeoPunkt: 3D-Koordinaten
  • Land: Nationale Ebene (mit dokumente, kontextInformationen als JSONB)
  • Kanton: Kantonale Ebene (mit dokumente, kontextInformationen als JSONB)
  • Gemeinde: Gemeinde-Ebene (mit dokumente, kontextInformationen als JSONB)

Automatische Tabellenerstellung

Wie funktioniert die automatische Tabellenerstellung?

Der DatabaseConnector erstellt Tabellen automatisch beim ersten Zugriff auf ein Pydantic-Modell. Sie müssen keine SQL-CREATE-TABLE-Statements manuell schreiben.

1. Ablauf der Tabellenerstellung:

1. Code ruft z.B. `db.recordCreate(Projekt, projekt_data)` auf
   ↓
2. DatabaseConnector ruft `_ensureTableExists(Projekt)` auf
   ↓
3. Prüft ob Tabelle "Projekt" existiert (über information_schema)
   ↓
4. Wenn NICHT vorhanden:
   → Ruft `_create_table_from_model()` auf
   → Extrahiert Felder aus Pydantic-Modell mit `_get_model_fields()`
   → Mappt Python-Typen zu SQL-Typen
   → Erstellt CREATE TABLE Statement
   → Führt SQL aus
   → Erstellt Indexes für Foreign Keys

2. Typ-Mapping (Python → PostgreSQL):

Der DatabaseConnector mappt automatisch Pydantic-Feldtypen zu PostgreSQL-Datentypen:

Python/Pydantic Typ PostgreSQL Typ Beispiel
str oder Optional[str] TEXT label: str"label" TEXT
int INTEGER vollgeschossZahl: int"vollgeschossZahl" INTEGER
float DOUBLE PRECISION az: float"az" DOUBLE PRECISION
bool BOOLEAN closed: bool"closed" BOOLEAN
Dict[str, Any] oder dict JSONB parameters: Dict[str, Any]"parameters" JSONB
List[...] oder list JSONB parzellen: List[Parzelle]"parzellen" JSONB
Optional[Enum] TEXT statusProzess: StatusProzess"statusProzess" TEXT

Spezielle Felder:

  • Felder mit Namen *Id (z.B. kontextKantonId) erhalten automatisch einen Index
  • Systemfelder werden automatisch hinzugefügt: _createdAt, _createdBy, _modifiedAt, _modifiedBy

3. Beispiel: CREATE TABLE Statement

Für das Projekt-Modell würde automatisch folgendes SQL erstellt:

CREATE TABLE IF NOT EXISTS "Projekt" (
    "id" VARCHAR(255) PRIMARY KEY,
    "mandateId" TEXT,
    "label" TEXT,
    "statusProzess" TEXT,
    "perimeter" JSONB,
    "baulinie" JSONB,
    "parzellen" JSONB,
    "dokumente" JSONB,
    "kontextInformationen" JSONB,
    "_createdAt" DOUBLE PRECISION,
    "_modifiedAt" DOUBLE PRECISION,
    "_createdBy" VARCHAR(255),
    "_modifiedBy" VARCHAR(255)
);

-- Automatisch erstellte Indexes für Foreign Keys:
CREATE INDEX IF NOT EXISTS "idx_Projekt_mandateId" ON "Projekt" ("mandateId");

4. Automatische Schema-Migrationen

Wichtig: Der Connector unterstützt additive Migrationen:

  • Wenn eine Tabelle bereits existiert, werden fehlende Spalten automatisch hinzugefügt
  • Bestehende Spalten werden NICHT gelöscht oder geändert
  • Wenn Sie ein neues Feld zum Pydantic-Modell hinzufügen, wird es beim nächsten Zugriff automatisch als Spalte hinzugefügt

Beispiel:

# Ursprüngliches Modell
class Projekt(BaseModel):
    id: str
    label: str
    statusProzess: Optional[StatusProzess]

# Später: Neues Feld hinzugefügt
class Projekt(BaseModel):
    id: str
    label: str
    statusProzess: Optional[StatusProzess]
    beschreibung: Optional[str]  # NEU

# Beim nächsten recordCreate() wird automatisch ausgeführt:
# ALTER TABLE "Projekt" ADD COLUMN "beschreibung" TEXT

5. Wann werden Tabellen erstellt?

Tabellen werden erstellt, wenn Sie zum ersten Mal eine der folgenden Operationen ausführen:

  • db.recordCreate(model_class, data) - Erstellt Record
  • db.recordUpdate(model_class, recordId, data) - Aktualisiert Record
  • db.getRecordset(model_class) - Lädt Records
  • db.getRecord(model_class, recordId) - Lädt einen Record

Beispiel:

# Beim ersten Aufruf wird die Tabelle "Projekt" automatisch erstellt
interface = getInterface(currentUser)
projekt = interface.createProjekt(label="Mein Projekt")
# → Tabelle "Projekt" wird jetzt in PostgreSQL erstellt

6. Manuelle Tabellenerstellung (optional)

Falls Sie Tabellen manuell erstellen möchten (z.B. für Initialisierung), können Sie:

from modules.connectors.connectorDbPostgre import DatabaseConnector
from modules.datamodels.datamodelRealEstate import Projekt, Parzelle

# Connector initialisieren
db = DatabaseConnector(
    dbHost="localhost",
    dbDatabase="poweron_app",
    dbUser="poweron_dev",
    dbPassword="...",
    dbPort=5432
)

# Tabellen explizit erstellen
db._ensureTableExists(Projekt)
db._ensureTableExists(Parzelle)
# ... weitere Modelle

7. Wichtige Hinweise:

Automatisch:

  • Tabellenerstellung beim ersten Zugriff
  • Spalten-Erstellung basierend auf Pydantic-Feldern
  • Index-Erstellung für Foreign Keys (*Id Felder)
  • Systemfelder (_createdAt, etc.) werden automatisch hinzugefügt

NICHT automatisch:

  • Foreign Key Constraints (werden nicht erstellt - Sie müssen sie manuell hinzufügen falls gewünscht)
  • Unique Constraints (außer PRIMARY KEY auf id)
  • Check Constraints
  • Trigger oder Stored Procedures

⚠️ Einschränkungen:

  • Keine Schema-Änderungen: Wenn Sie einen Feldtyp ändern (z.B. strint), wird die Spalte NICHT automatisch geändert
  • Keine Spalten-Löschung: Gelöschte Felder im Modell werden nicht aus der Datenbank entfernt
  • Case-Sensitive: Tabellennamen werden exakt wie der Klassenname verwendet (z.B. Projekt, nicht projekt)

8. Beispiel: Vollständiger Ablauf

# 1. Pydantic-Modell definieren
class Projekt(BaseModel):
    id: str = Field(default_factory=lambda: str(uuid.uuid4()))
    mandateId: str
    label: str
    statusProzess: Optional[StatusProzess]
    parzellen: List[Parzelle] = Field(default_factory=list)

# 2. Interface initialisieren (erstellt noch keine Tabellen)
interface = getInterface(currentUser)

# 3. Ersten Record erstellen (erstellt jetzt die Tabelle!)
projekt = interface.createProjekt(
    label="Mein erstes Projekt",
    statusProzess=StatusProzess.PLANUNG
)
# → Intern wird ausgeführt:
#   1. _ensureTableExists(Projekt) aufgerufen
#   2. Tabelle "Projekt" existiert nicht → wird erstellt
#   3. CREATE TABLE "Projekt" (...) wird ausgeführt
#   4. Record wird eingefügt

# 4. Weitere Records können jetzt ohne Tabellenerstellung erstellt werden
projekt2 = interface.createProjekt(label="Zweites Projekt")
# → Tabelle existiert bereits, nur INSERT wird ausgeführt

Zusammenfassung:

  • Tabellenname = Klassenname des Pydantic-Modells (z.B. Projekt)
  • Spalten = Alle Felder aus dem Pydantic-Modell
  • Typen = Automatisch gemappt (str→TEXT, List→JSONB, etc.)
  • Systemfelder = Automatisch hinzugefügt (_createdAt, _createdBy, etc.)
  • Indexes = Automatisch für Felder mit *Id Suffix
  • Migrationen = Additive Migrationen (neue Spalten werden hinzugefügt)
  • ⚠️ Keine Constraints = Foreign Keys, Unique, Check müssen manuell erstellt werden

Beispiel-Abfragen auf Real Estate-Datenmodell:

-- Alle Parzellen in einer bestimmten Gemeinde
SELECT * FROM Parzelle WHERE plz = '8000' ORDER BY label;

-- Projekte mit Status "Planung"
SELECT * FROM Projekt WHERE "statusProzess" = 'Planung';

-- Parzellen mit bestimmter Bauzone
SELECT label, az, bz, gebaeudehoeheMax FROM Parzelle WHERE bauzone = 'W3';

-- Dokumente eines Projekts
SELECT * FROM Dokument WHERE id IN (
    SELECT unnest(dokumente::jsonb->>'id') FROM Projekt WHERE id = '...'
);

← Zurück: Feature Lifecycle | Weiter: Sicherheitshinweise →