munin/internal/store/migrations/postgres/006_suchprotokoll.sql

46 lines
2.7 KiB
SQL

-- Suchprotokoll: Was Agenten mit "recall" suchen und ob sie etwas finden.
--
-- ── Warum? ───────────────────────────────────────────────────────────────────
--
-- Ohne diese Zahlen lässt sich nicht beurteilen, ob die Suche gut genug ist.
-- Eine Suche ohne Treffer ist das deutlichste Zeichen: Entweder gibt es das
-- Wissen nicht — dann ist die Anfrage ein Hinweis, was fehlt — oder es steht
-- unter anderen Wörtern da. Beides will man sehen (AUSBAUSTUFEN.md 3.1).
--
-- ── Warum nicht im Zugriffsprotokoll (audit_events)? ─────────────────────────
--
-- Gesucht wird viel öfter als geschrieben. Im Zugriffsprotokoll würden die
-- Suchen die Schreibvorgänge zudecken, um die es dort geht. Außerdem enthält
-- eine Anfrage Inhalt („wie kommt man an das Passwort für …"), und Inhalt hat
-- eine kürzere Aufbewahrungsfrist verdient als die 90 Tage des Protokolls:
-- Die tägliche Aufräumaufgabe löscht Zeilen nach 30 Tagen
-- (store.SearchLogRetention).
--
-- ── Aufbau ───────────────────────────────────────────────────────────────────
--
-- Eine Zeile je recall-Aufruf. Bewusst ohne IP-Adresse und ohne Namen: Für
-- die Auswertung zählt, WAS gesucht wurde, nicht wer. api_key_id bleibt, damit
-- sich ein einzelner Agent mit auffälligem Verhalten eingrenzen lässt.
--
-- Die ID ist hier eine fortlaufende Zahl (INTEGER PRIMARY KEY) statt eines
-- Zufallswerts wie in den anderen Tabellen: Niemand verweist auf eine
-- einzelne Zeile, und eine Zahl ist beim Einfügen am billigsten.
--
-- PostgreSQL-Fassung: "INTEGER PRIMARY KEY" zählt nur in SQLite von selbst
-- hoch. PostgreSQL braucht dafür ausdrücklich eine Identitätsspalte. BIGINT,
-- weil die Zahl nie zurückgesetzt wird, auch wenn alte Zeilen gelöscht werden.
--
-- Anders als das Zugriffsprotokoll hängt die Tabelle per Fremdschlüssel am
-- Projekt: Wird es gelöscht, verschwinden auch seine Anfragen. Sie sind Inhalt
-- des Projekts, kein Nachweis darüber, wer was getan hat.
CREATE TABLE search_log (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
ts TEXT NOT NULL,
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
api_key_id TEXT NOT NULL DEFAULT '',
query TEXT NOT NULL,
synonyms TEXT NOT NULL DEFAULT '', -- mit Komma getrennt, leer = keine
results INTEGER NOT NULL -- Zahl der ausgegebenen Treffer
);
CREATE INDEX idx_search_log_project ON search_log (project_id, ts);