Zum Inhalt springen

Suche und Filterung

Status: Entwurf · Spec-Kandidat: ja

Anforderungen

  • FR-008 – strukturierte Suche und Filterung (Muss).
  • FR-101 – Volltextsuche (Soll).
  • FR-105 – Dublettenprüfung (Soll).
  • NFR-003 – performante Listen- und Filterabfragen.

Mechanismen

Strukturierte Filter

  • Filter auf strukturellen Feldern (entity_type_id, status, code, name, created_at, updated_at).
  • Filter auf JSONB-Pfaden (attributes ->> 'industry' = 'banking').
  • Sortierung gesteuert durch DDM_ENTITY_TYPE_ATTRIBUTE.sortable.
  • Pagination: Cursor-basiert empfohlen (Detail in API-Spec).

Volltextsuche

  • DDM_ENTITY.search_vector wird durch Trigger gepflegt aus code, name und einer entzerrten Form von attributes.
  • Suche per search_vector @@ plainto_tsquery(...) o. ä.
  • Konfiguration simple ist Default; Anpassung pro Sprache offen.
  • Index: ix_entity_search_vector_gin.

Dublettenprüfung

  • Service-Layer-Logik auf Basis von DDM_ENTITY_TYPE_ATTRIBUTE.unique_per_type und ggf. zusätzlichen Heuristiken.
  • Bei Bedarf trigram-basierte Vergleiche (offen, erfordert pg_trgm).

Indexstrategie

  • Generischer GIN auf attributes als Fallback.
  • Gezielte Expression-Indizes für Attribute, die regelmäßig gefiltert oder durchsucht werden (siehe Indizes).
  • Volltext-GIN auf search_vector für Volltextsuche.
  • Steuerung: DDM_ENTITY_TYPE_ATTRIBUTE.searchable und filterable als deklarative Hinweise; konkrete Index-Erzeugung erfolgt über Migrationen oder Service-Layer-Tooling (offen).

Auswirkung auf API

  • Listenabfragen filtern automatisch deleted_at IS NULL (operativ).
  • Sonderpfad für Anzeige soft-gelöschter Datensätze (z. B. Audit) ist separat zu autorisieren.

Permission-Filter in Listenabfragen

Listen werden nicht post-gefiltert (Treffer holen, dann verwerfen). Der Permission-Check ist Teil der SQL-Query, damit Pagination und Counts korrekt sind und nur tatsächlich sichtbare Treffer zählen.

Pattern (Lese-Liste auf mdm.DDM_ENTITY für Action read):

SELECT e.*
FROM mdm.DDM_ENTITY e
WHERE e.tenant_id = $tenant
AND e.deleted_at IS NULL
AND e.entity_type_id = $type
-- deny gewinnt: kein anwendbarer deny darf existieren
AND NOT EXISTS (
SELECT 1 FROM mdm.v_effective_permission p
WHERE p.principal_type = $principal_type
AND p.principal_id = $principal_id
AND p.action = 'read'
AND p.effect = 'deny'
AND (
p.scope = 'global'
OR (p.scope = 'tenant' AND p.scope_tenant_id = e.tenant_id)
OR (p.scope = 'entity_type' AND p.scope_entity_type_id = e.entity_type_id)
OR (p.scope = 'entity' AND p.scope_entity_id = e.id)
)
)
-- mindestens ein allow muss existieren
AND EXISTS (
SELECT 1 FROM mdm.v_effective_permission p
WHERE p.principal_type = $principal_type
AND p.principal_id = $principal_id
AND p.action = 'read'
AND p.effect = 'allow'
AND (
p.scope = 'global'
OR (p.scope = 'tenant' AND p.scope_tenant_id = e.tenant_id)
OR (p.scope = 'entity_type' AND p.scope_entity_type_id = e.entity_type_id)
OR (p.scope = 'entity' AND p.scope_entity_id = e.id)
)
)
ORDER BY ...
LIMIT $page_size;

Hinweise:

  • Die zwei EXISTS/NOT EXISTS codieren die Auflösung aus Autorisierung: deny gewinnt immer, sonst genügt ein allow.
  • Indizes: (principal_type, principal_id, action, effect) auf AA_ACL_ENTRY und auf den RBAC-Auflösungsteil (siehe Indizes).
  • Helferfunktion empfohlen: mdm.fn_can(principal_type, principal_id, action, target_kind, target_id) als STABLE SQL-Funktion, die diese Logik kapselt — Service-Code joint dann nur WHERE mdm.fn_can(...).
  • Attribut-Maskierung (z. B. salary nur für HR sichtbar) erfolgt nach dem Filter durch das Service-Mapping, nicht im SQL.

Offen

  • Tooling zur automatischen Anlage / Pflege von Expression-Indizes auf Basis der Attribut-Flags.
  • Ranking / Relevanz für Volltextsuche.
  • Unscharfe Suche (pg_trgm) als Standard oder optional.
  • Externer Suchcluster (FR-204) als optionaler Pfad.

Verwandte Dokumente