Zum Hauptinhalt springen

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:

FeldBeschreibung
social_profile_idReferenz zum Profil
followers_countAktuelle Follower-Zahl
followers_growth_1d1-Tages-Wachstum (absolut)
followers_growth_7d7-Tages-Wachstum (absolut)
followers_growth_30d30-Tages-Wachstum (absolut)
trending_scoreTrending-Score (0-100)
growth_scoreGrowth-Score (0-100)
consistency_scoreKonsistenz der Datenverfuegbarkeit
latest_postbox_scoreDenormalisierter Postbox-Score (Plan 31)
latest_score_statusScore-Status: stable/pending/preliminary (Plan 31)
avg_video_scoreDurchschnittlicher Video-Performance-Score (Plan 31)
approved_contact_countAnzahl genehmigter Kontakt-Links (Plan 31)
follower_tiermicro / small / medium / large / mega
primary_categoryAuto-detektierte Kategorie
is_rising_starUeberdurchschnittliches Wachstum relativ zum Tier
is_newKuerzlich hinzugefuegtes Profil

Location: app/Models/ExploreProfileMetric.php

ExploreTrendingVideo

Trending-Video-Cache in explore_trending_videos:

FeldBeschreibung
youtube_video_idReferenz zum Video
social_profile_idZugehoeriges Profil
title, thumbnail_url, durationVideo-Metadaten
view_count, view_growth_1h, view_growth_24h, view_growth_7dView-Metriken
velocity_scoreViews pro Stunde
trending_scoreBerechneter 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.

FeldBeschreibung
slugURL-freundlicher Identifier (z.B. beauty-fashion, technology)
nameAI-Kategorie-Name (englisch, z.B. "Beauty & Fashion")
name_deDeutscher Anzeigename (z.B. "Beauty & Mode")
iconEmoji-Icon fuer UI-Anzeige
profile_countAnzahl zugeordneter Profile (aktualisiert bei Sync)
sort_orderSortierreihenfolge
is_activeOb 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

TierFollower-Bereich
micro0 - 10.000
small10.000 - 100.000
medium100.000 - 500.000
large500.000 - 1.000.000
mega1.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.

FeldBeschreibung
sectionSektionsname: top_scores, trending, growing, rising_stars, breakouts, new, trending_videos, videos
platformPlattform-Filter: all, youtube, instagram
profile_dataJSONB mit serialisierten Profil-Daten
calculated_atZeitpunkt 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

ServiceBeschreibung
ExploreMetricsCalculatorGrowth-Metriken, Trending-Scores, Rising Stars
ExploreTrendingVideosCalculatorVideo Velocity, View-Growth-Stats, Datenqualitaets-Monitoring
ExploreCategoryDetectorKategorie-Zuordnung via Keywords und YouTube Category IDs

Location: app/Services/Explore/ExploreMetricsCalculator.php, app/Services/Explore/ExploreTrendingVideosCalculator.php, app/Services/Explore/ExploreCategoryDetector.php

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): Gestrige youtube_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

Nach jeder Berechnung werden Plausibilitaets-Checks durchgefuehrt. Bei Anomalien wird Log::warning mit konkreten SQL-Diagnose-Queries geschrieben:

PruefungSchwellenwertDiagnose-Hinweis
Like-Coverage< 10% bei > 100 VideosYouTube API liefert moeglicherweise keine Like-Counts
24h-Growth-Coverage< 30% bei > 100 VideosVortags-Metriken fehlen in youtube_video_daily_metrics
Verarbeitete Videos< 100 (erwartet: 10.000+)Zu wenige Videos mit Metriken gefunden
Datum-FallbackGestern hat keine DatenSync-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;

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:

FilterOptionen
PlattformYouTube, Instagram
LandLaender mit min. 10 Profilen
Kategorie26 Auto-detektierte Kategorien
Follower Tiermicro, small, medium, large, mega
VerifizierungVerifiziert, Nicht verifiziert
PRONur PRO-Accounts
TagAI-generierte Keywords (mit Alias-Aufloesung)
Postbox ScoreScore-Range-Filter (min/max)

Sortieroptionen: Trending, Growth, Followers, Newest, Rising Stars, Score

Location: app/Livewire/Explore/Browse.php

Sub-Komponenten

KomponenteBeschreibung
ProfileGridWiederverwendbare Profil-Karten-Grid-Komponente (Lazy-Loading)
TrendingSectionTrending-Profil-Karussell (Lazy-Loading)
TrendingVideosTrending Videos mit Recency-/Duration-Filter
TagCloudTag-Wolke mit gewichteter Darstellung (Lazy-Loading)
CategoryCloudKategorie-Wolke mit Profil-Counts (Lazy-Loading)

Location: app/Livewire/Explore/, resources/views/livewire/explore/

Shared Trait: WithExploreHelpers

Gemeinsame Hilfsmethoden fuer alle Explore-Komponenten:

MethodeBeschreibung
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_verified fuer YouTube bleibt NULL

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_ops auf handle_normalized und title beschleunigen ILIKE '%term%' Queries erheblich (kein Sequential Scan)
  • Fuzzy-Matching: Bei aktiviertem pg_trgm wird zusaetzlich similarity() 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

FilterOptionenDefault
PlattformYouTube, Instagram, AlleAlle
LandLaender mit >= 10 ProfilenAlle
Kategorie26 AI-detektierte KategorienAlle
Follower Tiermicro, small, medium, large, megaAlle
  • 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/aus
  • toggleBulkSelect($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:

FeldBeschreibung
user_idUser, der gesucht hat
queryNormalisierter Suchbegriff
searched_atTimestamp der Suche
  • Dedupliziert per User + Query (unique constraint)
  • Wird automatisch aufgezeichnet in results Computed 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.