Explorar o código

refactor: remove unused cache key helpers and clean up video cache builder documentation

Dave hai 1 mes
pai
achega
b548137611

+ 0 - 17
libs/common/src/ads/cache-keys.ts

@@ -1,17 +0,0 @@
-// libs/common/src/ads/cache-keys.ts
-import type { AdType } from './ad-types';
-
-/** Build the canonical ad pool key for a given AdType. */
-function appAdPoolByType(adType: AdType): string;
-function appAdPoolByType(adType: string): string;
-function appAdPoolByType(adType: AdType | string): string {
-  return `app:adpool:${adType}`;
-}
-
-export const CacheKeys = {
-  appAdPoolByType,
-
-  appAd(id: string): string {
-    return `app:ad:${id}`;
-  },
-} as const;

+ 1 - 1
libs/common/src/cache/video-cache.utils.ts

@@ -1,5 +1,5 @@
 // libs/common/src/cache/video-cache.utils.ts
-/** 
+/**
  * Redis key helpers for video cache entries keyed by the canonical `box:app:*` namespace.
  */
 export const videoCacheKeys = {

+ 0 - 168
libs/core/src/cache/video/category/video-category-cache.builder.ts

@@ -14,61 +14,8 @@ import {
   type TagMetadata,
 } from '@box/common/cache/video-cache.helper';
 
-/**
- * Tag metadata payload for Redis cache (used in box:app:tag:list:{categoryId}).
- * ⚠️ IMPORTANT: This is for Tag metadata only. Video IDs are stored separately.
- */
 export interface TagMetadataPayload extends TagMetadata {}
 
-/**
- * Cache builder for video category/tag lists following new semantics.
- *
- * ═══════════════════════════════════════════════════════════════════════════
- * NEW SEMANTICS EXPLANATION
- * ═══════════════════════════════════════════════════════════════════════════
- *
- * This builder maintains THREE separate cache structures:
- *
- * 1. CATEGORY VIDEO LISTS
- *    Key: box:app:video:category:list:{categoryId}
- *    Type: LIST
- *    Contents: Video IDs ONLY (strings like "64a2b3c4d5e6f7...")
- *    ✅ CORRECT: Store only video IDs
- *    ❌ WRONG: Never store Tag JSON, Category JSON, or any metadata objects
- *    WHY: Video IDs are the minimal data needed to:
- *      - Return to client (they fetch full details separately)
- *      - Paginate in client (they use ZSET pools for ordering)
- *      - Filter videos by category quickly
- *
- * 2. TAG-FILTERED VIDEO LISTS
- *    Key: box:app:video:tag:list:{categoryId}:{tagId}
- *    Type: LIST
- *    Contents: Video IDs ONLY (subset of category videos with this tag)
- *    ✅ CORRECT: Store only video IDs (filtered by tag membership)
- *    ❌ WRONG: Never store Tag JSON or Video metadata
- *    WHY: Same as above - minimal data for tag-based filtering
- *
- * 3. TAG METADATA LISTS (THE ONLY PLACE FOR TAG JSON)
- *    Key: box:app:tag:list:{categoryId}
- *    Type: LIST
- *    Contents: Tag JSON objects (stringified)
- *    ✅ CORRECT: This is the ONLY key where Tag JSON should be stored
- *    ❌ WRONG: Never store Tag JSON in video list keys (items 1 & 2 above)
- *    WHY: Tags are used for filter UI (dropdown, checkboxes)
- *      - Client needs id, name, seq for display
- *      - Stored separately from videos to avoid duplication
- *      - Each element is parsed as JSON, not stored as raw strings
- *
- * ═══════════════════════════════════════════════════════════════════════════
- * KEY INVARIANT: NEVER store Tag/Category JSON in video list keys!
- * ═══════════════════════════════════════════════════════════════════════════
- *
- * WRITER CONTRACT:
- * ────────────────
- * - buildCategoryVideoListForCategory() → uses saveVideoIdList() → writes to box:app:video:category:list:*
- * - buildTagFilteredVideoListForTag() → uses saveVideoIdList() → writes to box:app:video:tag:list:*
- * - buildTagMetadataListForCategory() → uses saveTagList() → writes to box:app:tag:list:*
- */
 @Injectable()
 export class VideoCategoryCacheBuilder extends BaseCacheBuilder {
   private readonly cacheHelper: VideoCacheHelper;
@@ -84,22 +31,6 @@ export class VideoCategoryCacheBuilder extends BaseCacheBuilder {
    * 1. Category video lists (video IDs only, using saveVideoIdList)
    * 2. Tag-filtered video lists (video IDs only, using saveVideoIdList)
    * 3. Tag metadata lists (Tag JSON objects, using saveTagList)
-   *
-   * ⚠️ IMPORTANT IMPLEMENTATION DETAILS:
-   * ───────────────────────────────────
-   * - All writes go through VideoCacheHelper (never direct RedisService calls)
-   * - saveVideoIdList() is used for both category and tag-filtered video lists
-   * - saveTagList() is ONLY used for tag metadata (box:app:tag:list:{categoryId})
-   * - This ensures atomicity (DEL + RPUSH + EXPIRE) for all operations
-   * - Tag JSON is NEVER written to video list keys
-   *
-   * LOGGING:
-   * ────────
-   * Provides detailed statistics about cache rebuild:
-   * - Number of channels processed
-   * - Number of categories rebuilt (category video lists)
-   * - Number of tag-filtered video lists rebuilt
-   * - Number of tag metadata lists rebuilt
    */
   async buildAll(): Promise<void> {
     const channels = await this.mongoPrisma.channel.findMany();
@@ -178,32 +109,6 @@ export class VideoCategoryCacheBuilder extends BaseCacheBuilder {
 
   /**
    * Build category video list (LIST of video IDs only).
-   *
-   * QUERY STRATEGY:
-   * ──────────────
-   * - Fetch all videos with listStatus === 1 (on shelf)
-   * - Order by addedTime DESC (newest/most recently added first)
-   * - Fallback to createdAt DESC if addedTime is not set
-   * - Extract ONLY video IDs (no metadata)
-   *
-   * WRITE STRATEGY:
-   * ───────────────
-   * - Key: box:app:video:category:list:{categoryId}
-   * - Type: Redis LIST (ordered set of strings)
-   * - Helper: VideoCacheHelper.saveVideoIdList() for atomic DEL + RPUSH + EXPIRE
-   * - If no videos found: clear the key (DEL) and skip RPUSH
-   *
-   * ⚠️ CRITICAL CONTRACT:
-   * ────────────────────
-   * - ✅ ONLY store Video IDs (strings)
-   * - ❌ NEVER store Tag JSON, Category JSON, or any metadata objects
-   * - ❌ NEVER store Tag JSON in this key (use box:app:tag:list:{categoryId} instead)
-   *
-   * ERROR HANDLING:
-   * ───────────────
-   * - If no videos found: log debug message and clear the key
-   * - If Redis operation fails: log error with stack trace and re-throw
-   * - All operations are atomic (DEL + RPUSH + EXPIRE)
    */
   async buildCategoryVideoListForCategory(categoryId: string): Promise<number> {
     try {
@@ -237,33 +142,6 @@ export class VideoCategoryCacheBuilder extends BaseCacheBuilder {
 
   /**
    * Build tag-filtered video list (LIST of video IDs only).
-   *
-   * QUERY STRATEGY:
-   * ──────────────
-   * - Fetch all videos with listStatus === 1 (on shelf) AND tagIds contains this tagId
-   * - Order by addedTime DESC (newest/most recently added first)
-   * - Fallback to createdAt DESC if addedTime is not set
-   * - Extract ONLY video IDs (no metadata)
-   *
-   * WRITE STRATEGY:
-   * ───────────────
-   * - Key: box:app:video:tag:list:{categoryId}:{tagId}
-   * - Type: Redis LIST (ordered set of strings)
-   * - Helper: VideoCacheHelper.saveVideoIdList() for atomic DEL + RPUSH + EXPIRE
-   * - If no videos found: clear the key (DEL) and skip RPUSH
-   *
-   * ⚠️ CRITICAL CONTRACT:
-   * ────────────────────
-   * - ✅ ONLY store Video IDs (strings) that have this tag
-   * - ❌ NEVER store Tag JSON or metadata objects in this key
-   * - ❌ NEVER store Tag JSON (use box:app:tag:list:{categoryId} instead)
-   * - This key is DISTINCT from box:app:tag:list:{categoryId} which stores Tag JSON
-   *
-   * ERROR HANDLING:
-   * ───────────────
-   * - If no videos found with this tag: log debug message and clear the key
-   * - If Redis operation fails: log error with stack trace and re-throw
-   * - All operations are atomic (DEL + RPUSH + EXPIRE)
    */
   async buildTagFilteredVideoListForTag(
     categoryId: string,
@@ -304,52 +182,6 @@ export class VideoCategoryCacheBuilder extends BaseCacheBuilder {
 
   /**
    * Build tag metadata list (LIST of Tag JSON objects).
-   *
-   * QUERY STRATEGY:
-   * ──────────────
-   * - Fetch all tags with status === 1 (enabled) in this category
-   * - Order by seq ASC (business order for dropdown display)
-   * - Fallback to createAt ASC for consistent ordering
-   * - Transform to TagMetadataPayload (convert timestamps to strings)
-   *
-   * WRITE STRATEGY:
-   * ───────────────
-   * - Key: box:app:tag:list:{categoryId}
-   * - Type: Redis LIST (each element is stringified Tag JSON)
-   * - Helper: VideoCacheHelper.saveTagList() for atomic DEL + RPUSH + EXPIRE
-   * - If no tags found: clear the key (DEL) and skip RPUSH
-   *
-   * ⚠️ CRITICAL CONTRACT: THIS IS THE ONLY PLACE WHERE TAG JSON SHOULD BE STORED
-   * ──────────────────────────────────────────────────────────────────────────
-   * - ✅ ONLY store Tag JSON objects in this key
-   * - ✅ Each element is a stringified Tag object with {id, name, seq, status, ...}
-   * - ❌ NEVER store Tag JSON in "box:app:video:category:list:*" keys
-   * - ❌ NEVER store Tag JSON in "box:app:video:tag:list:*" keys
-   * - Video list keys MUST contain ONLY video IDs, never Tag metadata
-   *
-   * PURPOSE:
-   * ────────
-   * Used by frontend for tag filter UI (dropdowns, checkboxes)
-   * Each tag is a separate list element and parsed as JSON when read
-   *
-   * ERROR HANDLING:
-   * ───────────────
-   * - If no tags found: log debug message and clear the key
-   * - If Redis operation fails: log error with stack trace and re-throw
-   * - All operations are atomic (DEL + RPUSH + EXPIRE)
-   *
-   * FORMAT PER ELEMENT IN LIST:
-   * ──────────────────────────
-   * {
-   *   id: string (tag ID),
-   *   name: string (display name),
-   *   seq: number (sort order),
-   *   status: number (0=disabled, 1=enabled),
-   *   createAt: string (ISO timestamp),
-   *   updateAt: string (ISO timestamp),
-   *   channelId: string,
-   *   categoryId: string
-   * }
    */
   async buildTagMetadataListForCategory(categoryId: string): Promise<number> {
     try {

+ 0 - 62
libs/core/src/cache/video/list/video-list-cache.builder.ts

@@ -9,10 +9,6 @@ import type {
   VideoHomeSectionKey,
 } from '@box/common/cache/cache-keys';
 
-/**
- * Video payload for Redis pools (ZSET members).
- * Contains essential video metadata for display.
- */
 export interface VideoPoolPayload {
   id: string;
   title: string;
@@ -20,23 +16,6 @@ export interface VideoPoolPayload {
   tagIds?: string[];
 }
 
-/**
- * Cache builder for video pools and home sections.
- * Builds:
- *  - ZSET pools for videos by category (with sort: 'latest')
- *  - ZSET pools for videos by tag (with sort: 'latest')
- *  - LIST sections for home page (featured, latest, editorPick)
- *
- * Only includes videos where listStatus === 1 (on shelf).
- *
- * Strategy:
- *  1. For each channel, fetch all categories
- *  2. For each category, fetch all on-shelf videos
- *  3. Build ZSET pools indexed by (channelId, categoryId, 'latest')
- *  4. For each tag in the channel, fetch all on-shelf videos with that tag
- *  5. Build ZSET pools indexed by (channelId, tagId, 'latest')
- *  6. Build home section LISTs (top N recent videos across all categories in channel)
- */
 @Injectable()
 export class VideoListCacheBuilder extends BaseCacheBuilder {
   /** Top N videos to include in home section lists. */
@@ -55,9 +34,6 @@ export class VideoListCacheBuilder extends BaseCacheBuilder {
    *     - Build category pools (ZSET per category)
    *     - Build tag pools (ZSET per tag) - TODO: Refactor after schema change
    *     - Build home sections (LIST per section) - TODO: Refactor after schema change
-   *
-   * NOTE: After the schema change removing channelId from Category and Tag,
-   * the tag pools and home sections logic need to be refactored.
    */
   async buildAll(): Promise<void> {
     const channels = await this.mongoPrisma.channel.findMany();
@@ -81,15 +57,6 @@ export class VideoListCacheBuilder extends BaseCacheBuilder {
     );
   }
 
-  /**
-   * Build category pools (ZSET) for all categories in a channel.
-   * Groups videos by categoryId and creates a ZSET per category.
-   * Score: editedAt (converted to ms) or updatedAt as fallback.
-   * Sort order: descending (most recent first).
-   *
-   * NOTE: After schema change, categories are no longer tied to channels.
-   * This method now fetches all categories and builds pools without channel filtering.
-   */
   private async buildCategoryPoolsForChannel(channelId: string): Promise<void> {
     // Fetch all categories (no longer filtered by channelId)
     const categories = await this.mongoPrisma.category.findMany({
@@ -135,15 +102,6 @@ export class VideoListCacheBuilder extends BaseCacheBuilder {
     }
   }
 
-  /**
-   * Build tag pools (ZSET) for all tags in a channel.
-   * For each tag, fetches all on-shelf videos with that tag and creates a ZSET.
-   * Score: editedAt (converted to ms) or updatedAt as fallback.
-   * Sort order: descending (most recent first).
-   *
-   * TODO: Refactor after schema change - Tags no longer have channelId
-   * and need a new indexing strategy.
-   */
   private async buildTagPoolsForChannel(channelId: string): Promise<void> {
     // DEPRECATED: This method needs refactoring after the schema change.
     // Fetch all tags (no longer have channelId)
@@ -186,20 +144,6 @@ export class VideoListCacheBuilder extends BaseCacheBuilder {
     }
   }
 
-  /**
-   * Build home page sections (LIST) for a channel.
-   * For MVP, all sections return the top N most recent videos.
-   * Sections: featured, latest, editorPick
-   * Type: LIST of videoIds (in descending order by editedAt).
-   *
-   * TODO: Refactor after schema change - Categories no longer have channelId.
-   * Need new strategy to fetch home section videos.
-   *
-   * Process:
-   *  1. Fetch all on-shelf videos (no channel filtering)
-   *  2. Sort by editedAt desc, take top N
-   *  3. For each section, RPUSH the videoIds
-   */
   private async buildHomeSectionsForChannel(channelId: string): Promise<void> {
     // DEPRECATED: Fetch all categories (no longer filtered by channelId)
     const categories = await this.mongoPrisma.category.findMany({
@@ -242,12 +186,6 @@ export class VideoListCacheBuilder extends BaseCacheBuilder {
     }
   }
 
-  /**
-   * Get the score for ZSET ranking.
-   * Prefers editedAt (local edit time) if set and non-zero.
-   * Falls back to updatedAt (provider update time).
-   * Converts BigInt to number (milliseconds).
-   */
   private getVideoScore(video: any): number {
     // Prefer editedAt (local edit time) if set and non-zero
     if (video.editedAt) {