clickhouse-materialized-column-view-filter

Par divinevideo · divine-mobile

Corrigez les erreurs HTTP 500 / ClickHouse "column not found" lors du filtrage sur une colonne `MATERIALIZED` via une `VIEW` qui ne l'expose pas. À utiliser quand : (1) une clause `WHERE` référence `alias.colonne` sur une vue mais la colonne est `MATERIALIZED` sur la table sous-jacente, (2) la requête fonctionne sur la table brute mais échoue via la vue, (3) l'ajout d'un nouveau paramètre de filtre à une API provoque une erreur 500 alors que la colonne existe dans la table de base. Résolution : utiliser une sous-requête sur la table de base plutôt que de référencer la colonne directement sur la vue. S'applique aux vues ClickHouse sur des tables avec des colonnes `MATERIALIZED` ou `ALIAS`.

npx skills add https://github.com/divinevideo/divine-mobile --skill clickhouse-materialized-column-view-filter

ClickHouse : colonne MATERIALIZED non accessible via VIEW

Problème

Quand une VIEW ClickHouse sélectionne des colonnes spécifiques d'une table (pas SELECT *), les colonnes MATERIALIZED ou ALIAS non explicitement incluses dans la liste SELECT de la vue sont invisibles aux requêtes qui passent par la vue. Tenter WHERE v.materialized_col = ? sur une telle vue produit une erreur « column not found », qui remonte sous forme d'HTTP 500 dans les couches API.

Contexte / Conditions de déclenchement

  • Vous ajoutez un nouveau filtre de requête (par ex. ?platform=vine) qui référence une colonne via un alias de vue
  • La colonne est définie comme String MATERIALIZED ... sur la table sous-jacente
  • La VIEW a été créée avec une liste de colonnes explicite (pas SELECT *)
  • La colonne fonctionne correctement lors d'une requête directe sur la table de base
  • L'API retourne HTTP 500 sans message d'erreur utile au client
  • Les logs serveur affichent une erreur ClickHouse « column not found » ou similaire

Solution

Option A : Sous-requête (Aucune migration requise)

Remplacez la référence directe de colonne par une sous-requête contre la table de base :

-- CASSÉ : la vue n'expose pas 'platform'
WHERE v.platform = ?

-- CORRIGÉ : sous-requête contre la table de base où la colonne MATERIALIZED existe
WHERE v.id IN (
  SELECT id FROM events_deduped
  WHERE platform = ? AND kind IN (34235, 34236)
)

Option B : Migration (Plus propre à long terme)

Créez une nouvelle migration qui supprime et recrée la vue pour inclure la colonne :

DROP VIEW IF EXISTS nostr.videos;
CREATE VIEW nostr.videos AS
SELECT
    id, pubkey, created_at, kind, content, tags, sig, indexed_at,
    d_tag, title, thumbnail, video_url, author_name, loops,
    platform,  -- AJOUTER LA COLONNE MATERIALIZED
    if(published_at > 0, published_at, toUnixTimestamp(created_at)) AS published_at,
    expiration_at
FROM nostr.events_deduped FINAL
WHERE kind IN (34235, 34236);

Avertissement : La suppression d'une vue en cascade — toute vue dépendante (video_stats, trending_videos, videos_with_loops, etc.) doit aussi être supprimée et recréée dans le bon ordre de dépendances.

Vérification

  1. Interrogez la vue directement : SELECT platform FROM videos LIMIT 1 — devrait retourner des données (ou chaîne vide pour non-vine)
  2. L'appel API avec le paramètre de filtre retourne 200 au lieu de 500
  3. Exécutez la suite complète de tests de régression pour confirmer l'absence de régressions

Exemple (Funnelcake)

La vue nostr.videos (migration 000060) sélectionne une liste de colonnes fixe depuis events_deduped. La colonne platform est String MATERIALIZED sur events_deduped mais pas dans la vue.

La PR #85 a ajouté v.platform = ? à get_recent_videos_with_events() et get_trending_videos_with_events(), qui interrogent toutes deux FROM videos v. Cela a causé HTTP 500 pour toute requête avec ?platform=vine.

Correction (PR #86) : Changé pour l'approche sous-requête. Notez que videos_with_loops (une vue différente) INCLUT platform — les requêtes passant par cette vue (comme get_videos_filtered) fonctionnent correctement.

Notes

  • Les colonnes MATERIALIZED sont physiquement stockées mais accessibles uniquement si explicitement sélectionnées
  • Les colonnes ALIAS sont calculées à la lecture et ont la même contrainte de visibilité dans les vues
  • Vérifiez toujours la définition de la vue avant d'ajouter des conditions WHERE sur des colonnes
  • La vue videos_with_loops inclut plus de colonnes que videos — vérifiez quelle vue votre requête utilise réellement
  • Dans Funnelcake : vue videos = colonnes minimales ; videos_with_loops = colonnes complètes incluant platform

Skills similaires