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_vectorwird durch Trigger gepflegt auscode,nameund einer entzerrten Form vonattributes.- Suche per
search_vector @@ plainto_tsquery(...)o. ä. - Konfiguration
simpleist 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_typeund ggf. zusätzlichen Heuristiken. - Bei Bedarf trigram-basierte Vergleiche (offen, erfordert
pg_trgm).
Indexstrategie
- Generischer GIN auf
attributesals Fallback. - Gezielte Expression-Indizes für Attribute, die regelmäßig gefiltert oder durchsucht werden (siehe Indizes).
- Volltext-GIN auf
search_vectorfür Volltextsuche. - Steuerung:
DDM_ENTITY_TYPE_ATTRIBUTE.searchableundfilterableals 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 eWHERE 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 EXISTScodieren die Auflösung aus Autorisierung: deny gewinnt immer, sonst genügt ein allow. - Indizes:
(principal_type, principal_id, action, effect)aufAA_ACL_ENTRYund auf den RBAC-Auflösungsteil (siehe Indizes). - Helferfunktion empfohlen:
mdm.fn_can(principal_type, principal_id, action, target_kind, target_id)alsSTABLESQL-Funktion, die diese Logik kapselt — Service-Code joint dann nurWHERE mdm.fn_can(...). - Attribut-Maskierung (z. B.
salarynur 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.