Explore
Explore ermoeglicht die globale Entdeckung von Profilen unabhaengig vom eigenen Workspace. Die Seite bietet Trending-Profile, Growth-Rankings, Rising Stars, Trending Videos und kategoriebasierte Suche.
Datenmodell
ExploreProfileMetric
Vorberechnete Scores und Growth-Metriken pro Profil in explore_profile_metrics:
| Feld | Beschreibung |
|---|---|
social_profile_id | Referenz zum Profil |
followers_count | Aktuelle Follower-Zahl |
followers_growth_1d | 1-Tages-Wachstum (absolut) |
followers_growth_7d | 7-Tages-Wachstum (absolut) |
followers_growth_30d | 30-Tages-Wachstum (absolut) |
trending_score | Trending-Score (0-100) |
growth_score | Growth-Score (0-100) |
consistency_score | Konsistenz der Datenverfuegbarkeit |
latest_postbox_score | Denormalisierter Postbox-Score (Plan 31) |
latest_score_status | Score-Status: stable/pending/preliminary (Plan 31) |
avg_video_score | Durchschnittlicher Video-Performance-Score (Plan 31) |
approved_contact_count | Anzahl genehmigter Kontakt-Links (Plan 31) |
follower_tier | micro / small / medium / large / mega |
primary_category | Auto-detektierte Kategorie |
is_rising_star | Ueberdurchschnittliches Wachstum relativ zum Tier |
is_new | Kuerzlich hinzugefuegtes Profil |
Location: app/Models/ExploreProfileMetric.php
ExploreTrendingVideo
Trending-Video-Cache in explore_trending_videos:
| Feld | Beschreibung |
|---|---|
youtube_video_id | Referenz zum Video |
social_profile_id | Zugehoeriges Profil |
title, thumbnail_url, duration | Video-Metadaten |
view_count, view_growth_1h, view_growth_24h, view_growth_7d | View-Metriken |
velocity_score | Views pro Stunde |
trending_score | Berechneter Trending-Score |
Location: app/Models/ExploreTrendingVideo.php
ExploreCategory
Kategorie-Definitionen fuer die Explore-Filterung in explore_categories. Kategorien werden 1:1 aus dem AI-Enhancer (ChannelLanguageDetector::CATEGORIES) abgeleitet — kein Keyword-Matching mehr.
| Feld | Beschreibung |
|---|---|
slug | URL-freundlicher Identifier (z.B. beauty-fashion, technology) |
name | AI-Kategorie-Name (englisch, z.B. "Beauty & Fashion") |
name_de | Deutscher Anzeigename (z.B. "Beauty & Mode") |
icon | Emoji-Icon fuer UI-Anzeige |
profile_count | Anzahl zugeordneter Profile (aktualisiert bei Sync) |
sort_order | Sortierreihenfolge |
is_active | Ob die Kategorie im Frontend angezeigt wird |
26 Kategorien werden per Seeder aus ChannelLanguageDetector::CATEGORIES angelegt:
php artisan db:seed --class=ExploreCategorySeeder
Kategorien: Automotive, Beauty & Fashion, Comedy, Education, Finance & Business, Fitness & Health, Food & Cooking, Gaming, Lifestyle, Music, News, Politics, Pets & Animals, Science, Technology, Sports, Travel & Adventure, Kids & Family, Movies/TV/Anime, DIY/Crafts/Maker, Art & Design, Home & Garden, True Crime, ASMR & Relaxation, Podcasts & Interviews, Other.
Der ExploreCategoryDetector mappt social_profiles.ai_category (gesetzt durch den Gemini AI-Enhancer) auf den passenden Slug in explore_profile_metrics.primary_category. Statische Helper:
ExploreCategorySeeder::getSlugForAiCategory('Technology')→'technology'ExploreCategorySeeder::getAiCategoryForSlug('technology')→'Technology'
Location: app/Models/ExploreCategory.php, database/seeders/ExploreCategorySeeder.php
Follower Tiers
| Tier | Follower-Bereich |
|---|---|
| micro | 0 - 10.000 |
| small | 10.000 - 100.000 |
| medium | 100.000 - 500.000 |
| large | 500.000 - 1.000.000 |
| mega | 1.000.000+ |
Tiers werden sowohl fuer Explore-Filter als auch fuer Scoring verwendet. Die Definitionen sind identisch mit ProfileScoreCalculator::TIERS.
Performance-Optimierung (Plan 31)
ExploreQueryBuilder
Zentraler Query-Builder fuer alle Explore-Profil-Queries. Eliminiert Code-Duplikation zwischen Index.php und ProfileGrid.php. Nutzt denormalisierte Spalten statt mehrerer JOINs und Subqueries.
Location: app/Services/Explore/ExploreQueryBuilder.php
Section Cache (explore_section_cache)
Vorberechnete Profil-Daten fuer die Explore-Index-Sektionen. 8 Sektionen × 3 Plattformen (all, youtube, instagram) = 24 Eintraege.
| Feld | Beschreibung |
|---|---|
section | Sektionsname: top_scores, trending, growing, rising_stars, breakouts, new, trending_videos, videos |
platform | Plattform-Filter: all, youtube, instagram |
profile_data | JSONB mit serialisierten Profil-Daten |
calculated_at | Zeitpunkt der letzten Berechnung |
Wird nach explore:calculate --type=all automatisch via explore:cache-sections befuellt. Fallback auf Live-Query bei Nicht-Plattform-Filtern (Country, Category etc.).
Location: app/Console/Commands/CacheExploreSections.php
Watcher-Map Cache
loadAddedProfileIds() cached Watcher-Zuordnungen 5 Minuten pro User (Cache-Key: user:{id}:watcher_map). Wird bei Watcher-Hinzufuegen invalidiert.
Score-Window Konfiguration
Score-Berechnungsfenster ist konfigurierbar via config('postbox.scoring.score_window_days') (Default: 7 Tage). Genutzt in ExploreMetricsCalculator, CacheExploreSections und Index::scoreBreakouts().
Berechnung
Alle Explore-Metriken werden taeglich um 03:00 UTC berechnet:
# Alle Berechnungen ausfuehren
php artisan explore:calculate --type=all
# Einzelne Berechnungstypen
php artisan explore:calculate --type=daily # Growth-Metriken
php artisan explore:calculate --type=trending # Trending-Scores
php artisan explore:calculate --type=videos # Trending Videos
php artisan explore:calculate --type=categories # Kategorie-Detection
# Section-Cache separat befuellen
php artisan explore:cache-sections
Bei --type=all werden zusaetzlich automatisch die Schritte denormalize (Score/Video/Contact-Denormalisierung) und cache_sections (Section-Cache-Befuellung) ausgefuehrt.
Location: app/Console/Commands/CalculateExploreMetrics.php
Datenquelle
Die Berechnung basiert auf den letzten 4 abgeschlossenen DailySyncRun-Tagen (DailySyncRun.status = 'complete'). Das stellt sicher, dass bei 3-Tage-Rotation jedes Profil mindestens einen Datenpunkt hat.
Das "1-Tages-Wachstum" vergleicht die zwei juengsten Scrapes eines Profils, nicht Kalender-Tage.
Berechnungs-Services
| Service | Beschreibung |
|---|---|
ExploreMetricsCalculator | Growth-Metriken, Trending-Scores, Rising Stars |
ExploreTrendingVideosCalculator | Video Velocity, View-Growth-Stats, Datenqualitaets-Monitoring |
ExploreCategoryDetector | Kategorie-Zuordnung via Keywords und YouTube Category IDs |
Location: app/Services/Explore/ExploreMetricsCalculator.php, app/Services/Explore/ExploreTrendingVideosCalculator.php, app/Services/Explore/ExploreCategoryDetector.php
Trending Videos — Datum-Logik
Der ExploreTrendingVideosCalculator verwendet gestern als Baseline-Datum (nicht heute), da der YouTube-Sync-Job erst um 14:00 UTC laeuft und heutige Daten unvollstaendig waeren. Aufbau:
- Baseline (
$today): Gestrigeyoutube_video_daily_metrics— der letzte komplett gesyncte Tag - Vergleichstag (
$yesterday): Vorgestern — fuer 24h-Growth-Berechnung - Wochenbasis (
$weekAgo): 8 Tage zurueck — fuer 7d-Growth-Berechnung - Fallback: Wenn gestern keine Daten existieren (z.B. Sync komplett ausgefallen), wird
max(date)als Baseline verwendet und eine Warning geloggt
Trending Videos — Datenqualitaets-Monitoring
Nach jeder Berechnung werden Plausibilitaets-Checks durchgefuehrt. Bei Anomalien wird Log::warning mit konkreten SQL-Diagnose-Queries geschrieben:
| Pruefung | Schwellenwert | Diagnose-Hinweis |
|---|---|---|
| Like-Coverage | < 10% bei > 100 Videos | YouTube API liefert moeglicherweise keine Like-Counts |
| 24h-Growth-Coverage | < 30% bei > 100 Videos | Vortags-Metriken fehlen in youtube_video_daily_metrics |
| Verarbeitete Videos | < 100 (erwartet: 10.000+) | Zu wenige Videos mit Metriken gefunden |
| Datum-Fallback | Gestern hat keine Daten | Sync-Job ist moeglicherweise nicht gelaufen |
Jeder Log-Eintrag enthaelt fertige SQL-Queries zur Diagnose, z.B.:
SELECT date, COUNT(*) FROM youtube_video_daily_metrics
WHERE date >= CURRENT_DATE - INTERVAL '3 days'
GROUP BY date ORDER BY date;
Trending Score
Der Trending Score (0-100) wird aus drei Faktoren berechnet:
- 1-Tages-Wachstum
- Growth Score
- Aktivitaet (Datenverfuegbarkeit)
Profile im oberen 25. Perzentil erhalten das Trending-Flag.
Rising Stars
Ein Profil ist ein Rising Star, wenn es ueberdurchschnittliches Wachstum relativ zu seinem Tier aufweist:
- Micro/Small: >5% woechentliches Wachstum
- Medium/Large/Mega: >3% woechentliches Wachstum
Ausschluss-Filter
Bei allen Explore-Queries muessen excluded Profile gefiltert werden:
->whereNull('blocked_at')
->whereNull('sanitized_at')
->whereNull('archived_at')
// oder via Scope:
->notExcluded()
UI-Komponenten
Explore Index (/explore)
Startseite mit Sektionen:
- Trending-Profile (Karussell)
- Fastest Growing
- Rising Stars
- Trending Videos
- Neue Profile
Location: app/Livewire/Explore/Index.php
Browse Page (/explore/browse)
Paginiertes Grid mit Filtern:
| Filter | Optionen |
|---|---|
| Plattform | YouTube, Instagram |
| Land | Laender mit min. 10 Profilen |
| Kategorie | 26 Auto-detektierte Kategorien |
| Follower Tier | micro, small, medium, large, mega |
| Verifizierung | Verifiziert, Nicht verifiziert |
| PRO | Nur PRO-Accounts |
| Tag | AI-generierte Keywords (mit Alias-Aufloesung) |
| Postbox Score | Score-Range-Filter (min/max) |
Sortieroptionen: Trending, Growth, Followers, Newest, Rising Stars, Score
Location: app/Livewire/Explore/Browse.php
Sub-Komponenten
| Komponente | Beschreibung |
|---|---|
ProfileGrid | Wiederverwendbare Profil-Karten-Grid-Komponente (Lazy-Loading) |
TrendingSection | Trending-Profil-Karussell (Lazy-Loading) |
TrendingVideos | Trending Videos mit Recency-/Duration-Filter |
TagCloud | Tag-Wolke mit gewichteter Darstellung (Lazy-Loading) |
CategoryCloud | Kategorie-Wolke mit Profil-Counts (Lazy-Loading) |
Location: app/Livewire/Explore/, resources/views/livewire/explore/
Shared Trait: WithExploreHelpers
Gemeinsame Hilfsmethoden fuer alle Explore-Komponenten:
| Methode | Beschreibung |
|---|---|
formatCount(int $count) | Formatiert Zahlen als 1,5K / 1,5M |
getProfileImageUrl(SocialProfile $profile) | Gibt Large-Variante der Profil-URL zurueck |
loadAddedProfileIds(array $profileIds) | Laed Watcher-ID-Mappings ueber alle User-Workspaces |
Location: app/Concerns/WithExploreHelpers.php
Cache TTLs
Filter-Daten (Laender, Kategorien, Statistiken) werden fuer 24 Stunden gecacht, passend zum taeglichen Berechnungs-Zyklus.
Bekannte Einschraenkungen
YouTube Verification Status: Die YouTube Data API liefert keinen Verifikations-Status. Daher:
- Filter "Verifiziert" liefert nur Instagram-Profile
- Filter "Nicht verifiziert" schliesst alle YouTube-Profile ein
is_verifiedfuer YouTube bleibtNULL
Suche & Discover: Globale Profil-Suche
Die Suche & Discover-Seite (/discover) ermoeglicht globale Profilsuche mit Volltextmatching und erweiterter Filterung. Sie erganzt Explore mit einer Such-zentrierten Erfahrung statt kuratierten Sektionen.
Suchverhalten
Volltextsuche (PostgreSQL ILIKE + Trigram Fuzzy-Matching):
- Durchsucht
handle,handle_normalized,title,description(case-insensitive) - Trigram-Indexes: GIN-Indexes mit
gin_trgm_opsaufhandle_normalizedundtitlebeschleunigenILIKE '%term%'Queries erheblich (kein Sequential Scan) - Fuzzy-Matching: Bei aktiviertem
pg_trgmwird zusaetzlichsimilarity()mit Threshold 0.3 verwendet fuer Tippfehler-tolerante Suche - Min. Query-Laenge: 2 Zeichen
- Suchhistorie wird fuer jedes User aufgezeichnet (nur bei Resultaten)
- Letzte 10 Searches im UI (Link zum Wiederverwenden)
PostgreSQL-Voraussetzung: Die Extension pg_trgm muss aktiviert sein:
sudo -u postgres psql -d postbox_db1 -c "CREATE EXTENSION IF NOT EXISTS pg_trgm;"
Rate Limiting:
- Max. 5 neue Suchanfragen pro 10 Sekunden pro User
- Begrenzt nur neue Queries, nicht Filter-Aenderungen
- Bei Limit: Toast-Warning "Zu viele Suchanfragen. Bitte warte einen Moment."
Filter & Pagination
| Filter | Optionen | Default |
|---|---|---|
| Plattform | YouTube, Instagram, Alle | Alle |
| Land | Laender mit >= 10 Profilen | Alle |
| Kategorie | 26 AI-detektierte Kategorien | Alle |
| Follower Tier | micro, small, medium, large, mega | Alle |
- Alle Filter sind URL-Parameter (deep-linkable)
- Filter-Aenderungen reset Pagination (limit = 24) und Bulk-Selection
- Basis-Query:
WHERE blocked_at IS NULL AND sanitized_at IS NULL AND archived_at IS NULL - Min. Follower-Threshold fuer Discovery angewendet
Bulk-Select & Multi-Add
Modus:
toggleBulkMode()schaltet Bulk-UI ein/austoggleBulkSelect($profileId)togglet einzelne Profile- Auswahl clearen bei Filter-Aenderung oder Mode-Toggle
Workspace-Modal:
- Single-Add: Modal nur wenn User >1 Workspace hat
- Bulk-Add: Modal zeigt sich, um Ziel-Workspace zu waehlen
- Duplikat-Check: Profile die bereits im Workspace existieren, werden skipped
- Toast mit Erfolgscount
Datenmodell: SearchHistory
Tabelle search_histories:
| Feld | Beschreibung |
|---|---|
user_id | User, der gesucht hat |
query | Normalisierter Suchbegriff |
searched_at | Timestamp der Suche |
- Dedupliziert per User + Query (unique constraint)
- Wird automatisch aufgezeichnet in
resultsComputed Property SearchHistory::recent($userId, 10)liefert letzte 10 Suchanfragen
Location: app/Models/SearchHistory.php
Implementation
Service: ProfileSearchService baut dynamische Query mit Filtern
Component: Discover\Index managed UI-State, Rate Limiting, Workspace-Modal
View: resources/views/livewire/discover/index.blade.php
Siehe auch: /docs/09-livewire/06-explore.md#discoverindex fuer vollständige Component-Details.