Watchers/Index
Watcher-Liste eines Workspaces mit Multi-Import, Suche, Filtern, Sortierung und Bulk-Operationen.
Route: /watchers, /workspace/{workspace}
View: resources/views/livewire/watchers/index.blade.php
Location: app/Livewire/Watchers/Index.php
Public Properties
| Property | Typ | Default | Query String | Beschreibung |
|---|---|---|---|---|
$q | string | '' | ja | Suchbegriff (Name, Handle, mit Trigram-Fuzzy auf PostgreSQL) |
$platform | string | '' | ja | Plattform-Filter: '', 'youtube', 'instagram' |
$sort | string | 'score_desc' | ja | Sortierung (siehe Sortier-Optionen). Admin-Workspace: 'created_desc' |
$favoritesFilter | string | 'all' | ja | 'all' oder 'favorites' |
$relatedFilter | string | '' | ja | '', 'with', 'without' (nur Admin-Workspace) |
$verifiedFilter | string | '' | ja | '', 'verified', 'unverified' |
$perPage | int | 25 | nein | Eintraege pro Seite |
$page | int | 1 | ja | Aktuelle Seite |
$multiImportText | string | '' | nein | Textarea-Inhalt fuer Multi-Import |
$multiImportOpen | bool | false | nein | Multi-Import Modal offen |
$multiImportCsv | array | [] | nein | Hochgeladene CSV-Dateien |
$importRunId | int|null | null | nein | Aktive Import-Run ID |
$selectedWatcherIds | array | [] | nein | Ausgewaehlte Watcher fuer Bulk-Ops |
$selectAllWatchers | bool | false | nein | Select-All Checkbox State |
$moveWorkspaceId | int|null | null | nein | Ziel-Workspace fuer Verschieben |
$deletingWatcherId | int|null | null | nein | Watcher-ID fuer Loeschen-Bestaetigung |
$showWorkspaceEditModal | bool | false | nein | Workspace-Bearbeitung Modal |
Sortier-Optionen
| Wert | Beschreibung |
|---|---|
name_asc / name_desc | Alphabetisch nach Name |
followers_desc / followers_asc | Nach Follower-Anzahl |
views_desc / views_asc | Nach View-Anzahl |
videos_desc / videos_asc | Nach Video-Anzahl |
following_desc / following_asc | Nach Following-Anzahl |
posts_desc / posts_asc | Nach Post-Anzahl |
score_desc / score_asc | Nach Postbox Score |
created_desc / created_asc | Nach Erstellungsdatum |
Sortierung nutzt Subqueries auf SocialProfileDailyMetric und SocialProfileScore fuer performante Sortierung ohne JOINs.
Default-Sort: score_desc fuer normale Workspaces, created_desc fuer den Admin-Workspace (ID 999999999999). Die Umschaltung passiert in mount() und wird nur angewendet, wenn kein expliziter ?sort=-Parameter in der URL steht.
Performance-Analyse der Sortier-Varianten
Die render()-Methode haengt 18 korrelierte Subqueries im addSelect() an die Watcher-Query. Diese Subqueries holen fuer jede Row die aktuellen Metriken, Score-Werte und Profil-Daten. Das Verhalten dieser Subqueries haengt entscheidend vom gewahlten Sort ab.
Warum created_desc der schnellste Sort ist
created_desc sortiert mit ORDER BY id DESC — PostgreSQL kann hier den PK-Index rueckwaerts scannen, nach workspace_id filtern und nach dem LIMIT (z.B. 25) sofort stoppen. Die 18 SELECT-Subqueries werden nur fuer die ~25 Seiten-Rows ausgewertet. Alle anderen Sorts erfordern ein berechnetes Feld im ORDER BY, was PostgreSQL zwingt alle Watcher des Workspace zu evaluieren, bevor das LIMIT greifen kann.
Geschaetzte Response-Zeiten
| Sort-Variante | 1.000 Watcher | 10.000 Watcher | Strategie |
|---|---|---|---|
created_desc / created_asc | ~15–30 ms | ~20–40 ms | PK-Index, Early-Stop |
followers_desc / views_desc | ~300–500 ms | ~3–6 s | Full-Scan, Metriken-Subquery im ORDER BY |
name_asc / name_desc | ~250–450 ms | ~2.5–5 s | Full-Scan, kein Index auf name |
score_desc / score_asc | ~350–600 ms | ~3.5–7 s | Full-Scan, 2-Level Sort (Priority + Score) |
Kostentreiber
| Faktor | Auswirkung |
|---|---|
| 18 korrelierte Subqueries | ~1 ms pro Row (Index-Seeks auf social_profile_daily_metrics, social_profile_scores) |
| Full-Scan bei non-PK Sort | Alle N Rows muessen evaluiert werden, LIMIT greift erst nach Sort |
score_sort_priority | Zusaetzliche Subquery mit CASE-Ausdruck auf social_profiles |
Trigram-Suche (pg_trgm) | +20–50% wenn Suchterm gesetzt, unabhaengig vom Sort |
Admin-Workspace Optimierung
Der Admin-Workspace spiegelt alle ~40.000+ Social Profiles. Bei score_desc als Default wuerden pro Seitenaufruf ~40.000 × 19 Subqueries evaluiert, was Response-Zeiten von 10+ Sekunden verursacht. Mit created_desc als Default nutzt der Admin-Workspace den PK-Index und laed in ~30 ms. Admins koennen weiterhin per Dropdown auf jede andere Sortierung wechseln.
Location: app/Livewire/Watchers/Index.php — mount() (Default-Override), applySorting() (Sort-Logik)
Actions
| Method | Parameter | Beschreibung |
|---|---|---|
submitMultiImport() | -- | Multi-Import starten (Text + CSV) |
openMultiImport() | -- | Import-Modal oeffnen |
closeMultiImport() | -- | Import-Modal schliessen |
confirmDelete(int $id) | Watcher-ID | Loeschen-Bestaetigung oeffnen |
delete() | -- | Watcher loeschen (Sources + Watcher, Profil bleibt) |
moveSelectedWatchers() | -- | Ausgewaehlte Watcher in Ziel-Workspace verschieben |
resetFilters() | -- | Alle Filter zuruecksetzen |
dismissImportReport() | -- | Import-Report-Banner ausblenden |
openWorkspaceEdit() | -- | Workspace-Bearbeitung oeffnen |
saveWorkspaceEdit() | -- | Workspace-Name/Beschreibung speichern |
Multi-Import
Der Import unterstuetzt mehrere Eingabewege:
- Textarea: URLs zeilenweise eingeben (max. 10.000 Zeilen, 750KB)
- CSV-Upload: Eine oder mehrere CSV-Dateien mit URLs in beliebigen Spalten
- Kombination: Textarea + CSV gleichzeitig
Ablauf:
URLs eingeben/hochladen
→ parseMultiImportLines() erkennt YouTube/Instagram via Parser
→ Deduplizierung innerhalb des Inputs
→ WatcherImportRun + WatcherImportItems erstellen
→ ImportWatcherFromUrl Jobs dispatchen (YouTube: imports-youtube-priority)
→ CollectorJobDispatcher fuer Instagram
→ Import-Report Banner anzeigen
Geschuetzte Workspaces (Favoriten, Admin) blockieren den Import mit Fehlermeldung.
Events
| Richtung | Event | Beschreibung |
|---|---|---|
| dispatch | show-toast | Erfolgs-/Fehlermeldungen |
| dispatch | workspaces-updated | Nach Workspace-Umbenennung |
| listen | .import.progress | WatcherImportProgress via Reverb |
Suche
Die Suche nutzt ILIKE auf Watcher-Name und Profil-Handle. Auf PostgreSQL mit pg_trgm-Extension wird zusaetzlich Fuzzy-Matching via similarity() mit Threshold 0.3 aktiviert. SQLite-Fallback nutzt nur ILIKE.
Render-Logik
Die render()-Methode baut eine komplexe Query mit Subqueries fuer alle metrischen Felder (followers, views, videos, following, posts, score) und den Primary-Profile-Daten. Das vermeidet N+1-Queries und ermoeglicht serverseitige Sortierung ueber berechnete Spalten.