diff --git a/apps/backend/src/db/migrations/20260213040541_create_indexes.sql b/apps/backend/src/db/migrations/20260213040541_create_indexes.sql new file mode 100644 index 00000000..69a72af5 --- /dev/null +++ b/apps/backend/src/db/migrations/20260213040541_create_indexes.sql @@ -0,0 +1,7 @@ +CREATE INDEX "idx_followers_follower_id" ON "followers" USING btree ("follower_id");--> statement-breakpoint +CREATE INDEX "idx_followers_followed_id" ON "followers" USING btree ("followed_id");--> statement-breakpoint +CREATE INDEX "idx_reviews_user_rating" ON "reviews" USING btree ("user_id","rating");--> statement-breakpoint +CREATE INDEX "idx_user_episodes_user_id" ON "user_episodes" USING btree ("user_id");--> statement-breakpoint +CREATE INDEX "idx_user_episodes_user_tmdb" ON "user_episodes" USING btree ("user_id","tmdb_id");--> statement-breakpoint +CREATE INDEX "idx_user_items_user_status" ON "user_items" USING btree ("user_id","status");--> statement-breakpoint +CREATE INDEX "idx_user_items_user_media_status" ON "user_items" USING btree ("user_id","media_type","status"); \ No newline at end of file diff --git a/apps/backend/src/db/migrations/meta/20260213040541_snapshot.json b/apps/backend/src/db/migrations/meta/20260213040541_snapshot.json new file mode 100644 index 00000000..6fd6f99f --- /dev/null +++ b/apps/backend/src/db/migrations/meta/20260213040541_snapshot.json @@ -0,0 +1,1801 @@ +{ + "id": "ef88eba3-9e21-45ed-bd45-c4ce4edee094", + "prevId": "10720c5c-1d7e-457e-afcb-34840a993838", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.followers": { + "name": "followers", + "schema": "", + "columns": { + "follower_id": { + "name": "follower_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "followed_id": { + "name": "followed_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_followers_follower_id": { + "name": "idx_followers_follower_id", + "columns": [ + { + "expression": "follower_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_followers_followed_id": { + "name": "idx_followers_followed_id", + "columns": [ + { + "expression": "followed_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "followers_follower_id_users_id_fk": { + "name": "followers_follower_id_users_id_fk", + "tableFrom": "followers", + "tableTo": "users", + "columnsFrom": [ + "follower_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "followers_followed_id_users_id_fk": { + "name": "followers_followed_id_users_id_fk", + "tableFrom": "followers", + "tableTo": "users", + "columnsFrom": [ + "followed_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "followers_followed_id_follower_id_pk": { + "name": "followers_followed_id_follower_id_pk", + "columns": [ + "followed_id", + "follower_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.import_movies": { + "name": "import_movies", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "import_id": { + "name": "import_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "end_date": { + "name": "end_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "item_status": { + "name": "item_status", + "type": "status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "import_status": { + "name": "import_status", + "type": "import_item_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "TMDB_ID": { + "name": "TMDB_ID", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "__metadata": { + "name": "__metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "import_movies_import_id_user_imports_id_fk": { + "name": "import_movies_import_id_user_imports_id_fk", + "tableFrom": "import_movies", + "tableTo": "user_imports", + "columnsFrom": [ + "import_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.import_series": { + "name": "import_series", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "import_id": { + "name": "import_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "start_date": { + "name": "start_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "end_date": { + "name": "end_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "item_status": { + "name": "item_status", + "type": "status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "import_status": { + "name": "import_status", + "type": "import_item_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "TMDB_ID": { + "name": "TMDB_ID", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "watched_episodes": { + "name": "watched_episodes", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "series_episodes": { + "name": "series_episodes", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "__metadata": { + "name": "__metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "import_series_import_id_user_imports_id_fk": { + "name": "import_series_import_id_user_imports_id_fk", + "tableFrom": "import_series", + "tableTo": "user_imports", + "columnsFrom": [ + "import_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.likes": { + "name": "likes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "entity_id": { + "name": "entity_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "entity_type": { + "name": "entity_type", + "type": "like_entity", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_entity_id": { + "name": "idx_entity_id", + "columns": [ + { + "expression": "entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "likes_user_id_users_id_fk": { + "name": "likes_user_id_users_id_fk", + "tableFrom": "likes", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.list_items": { + "name": "list_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "list_id": { + "name": "list_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "tmdb_id": { + "name": "tmdb_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "media_type": { + "name": "media_type", + "type": "media_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "position": { + "name": "position", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "list_items_list_id_lists_id_fk": { + "name": "list_items_list_id_lists_id_fk", + "tableFrom": "list_items", + "tableTo": "lists", + "columnsFrom": [ + "list_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "list_items_id_list_id_pk": { + "name": "list_items_id_list_id_pk", + "columns": [ + "id", + "list_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.lists": { + "name": "lists", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "banner_url": { + "name": "banner_url", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "visibility": { + "name": "visibility", + "type": "list_visibility", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "user_id_idx": { + "name": "user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "lists_user_id_users_id_fk": { + "name": "lists_user_id_users_id_fk", + "tableFrom": "lists", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.magic_tokens": { + "name": "magic_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "used": { + "name": "used", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": { + "token_user_id_idx": { + "name": "token_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "token_idx": { + "name": "token_idx", + "columns": [ + { + "expression": "token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "magic_tokens_user_id_users_id_fk": { + "name": "magic_tokens_user_id_users_id_fk", + "tableFrom": "magic_tokens", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.review_replies": { + "name": "review_replies", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "reply": { + "name": "reply", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "review_id": { + "name": "review_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "review_replies_user_id_users_id_fk": { + "name": "review_replies_user_id_users_id_fk", + "tableFrom": "review_replies", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "review_replies_review_id_reviews_id_fk": { + "name": "review_replies_review_id_reviews_id_fk", + "tableFrom": "review_replies", + "tableTo": "reviews", + "columnsFrom": [ + "review_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reviews": { + "name": "reviews", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "tmdb_id": { + "name": "tmdb_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "media_type": { + "name": "media_type", + "type": "media_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "review": { + "name": "review", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "rating": { + "name": "rating", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "has_spoilers": { + "name": "has_spoilers", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "language": { + "name": "language", + "type": "languages", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "season_number": { + "name": "season_number", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "episode_number": { + "name": "episode_number", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_reviews_user_rating": { + "name": "idx_reviews_user_rating", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "rating", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reviews_user_id_users_id_fk": { + "name": "reviews_user_id_users_id_fk", + "tableFrom": "reviews", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.social_links": { + "name": "social_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "platform": { + "name": "platform", + "type": "social_platforms", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "social_links_user_id_users_id_fk": { + "name": "social_links_user_id_users_id_fk", + "tableFrom": "social_links", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_platform_unique": { + "name": "user_platform_unique", + "nullsNotDistinct": false, + "columns": [ + "user_id", + "platform" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.subscriptions": { + "name": "subscriptions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "subscription_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "subscription_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'ACTIVE'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "canceled_at": { + "name": "canceled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancellation_reason": { + "name": "cancellation_reason", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "active_subscription_idx": { + "name": "active_subscription_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"subscriptions\".\"status\" = $1", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "subscriptions_user_id_users_id_fk": { + "name": "subscriptions_user_id_users_id_fk", + "tableFrom": "subscriptions", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_activities": { + "name": "user_activities", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "activity_type": { + "name": "activity_type", + "type": "activity_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "entity_id": { + "name": "entity_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "entity_type": { + "name": "entity_type", + "type": "like_entity", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "user_activity_idx": { + "name": "user_activity_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_activities_user_id_users_id_fk": { + "name": "user_activities_user_id_users_id_fk", + "tableFrom": "user_activities", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_episodes": { + "name": "user_episodes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "tmdb_id": { + "name": "tmdb_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "season_number": { + "name": "season_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "episode_number": { + "name": "episode_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "watched_at": { + "name": "watched_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "runtime": { + "name": "runtime", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "idx_user_episodes_user_id": { + "name": "idx_user_episodes_user_id", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_user_episodes_user_tmdb": { + "name": "idx_user_episodes_user_tmdb", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "tmdb_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_episodes_user_id_users_id_fk": { + "name": "user_episodes_user_id_users_id_fk", + "tableFrom": "user_episodes", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_episode_unique": { + "name": "user_episode_unique", + "nullsNotDistinct": false, + "columns": [ + "user_id", + "tmdb_id", + "season_number", + "episode_number" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_imports": { + "name": "user_imports", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "items_count": { + "name": "items_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "import_status": { + "name": "import_status", + "type": "import_status_enum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "providers_enum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "user_imports_user_id_users_id_fk": { + "name": "user_imports_user_id_users_id_fk", + "tableFrom": "user_imports", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_items": { + "name": "user_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "tmdb_id": { + "name": "tmdb_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "media_type": { + "name": "media_type", + "type": "media_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "added_at": { + "name": "added_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_user_items_user_status": { + "name": "idx_user_items_user_status", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_user_items_user_media_status": { + "name": "idx_user_items_user_media_status", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "media_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_items_user_id_users_id_fk": { + "name": "user_items_user_id_users_id_fk", + "tableFrom": "user_items", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_items_userid_tmdbid_media_type_unique": { + "name": "user_items_userid_tmdbid_media_type_unique", + "nullsNotDistinct": false, + "columns": [ + "user_id", + "tmdb_id", + "media_type" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_watch_entries": { + "name": "user_watch_entries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_item_id": { + "name": "user_item_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "watched_at": { + "name": "watched_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "user_watch_entries_user_item_idx": { + "name": "user_watch_entries_user_item_idx", + "columns": [ + { + "expression": "user_item_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_watch_entries_user_item_id_user_items_id_fk": { + "name": "user_watch_entries_user_item_id_user_items_id_fk", + "tableFrom": "user_watch_entries", + "tableTo": "user_items", + "columnsFrom": [ + "user_item_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "banner_url": { + "name": "banner_url", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "avatar_url": { + "name": "avatar_url", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "is_legacy": { + "name": "is_legacy", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "biography": { + "name": "biography", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "username_lower_idx": { + "name": "username_lower_idx", + "columns": [ + { + "expression": "LOWER(\"username\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "email_lower_idx": { + "name": "email_lower_idx", + "columns": [ + { + "expression": "LOWER(\"email\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_username_unique": { + "name": "users_username_unique", + "nullsNotDistinct": false, + "columns": [ + "username" + ] + }, + "users_email_unique": { + "name": "users_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_preferences": { + "name": "user_preferences", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "watch_providers_ids": { + "name": "watch_providers_ids", + "type": "integer[]", + "primaryKey": false, + "notNull": false + }, + "watch_region": { + "name": "watch_region", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "media_types": { + "name": "media_types", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "genre_ids": { + "name": "genre_ids", + "type": "integer[]", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "user_preferences_user_id_users_id_fk": { + "name": "user_preferences_user_id_users_id_fk", + "tableFrom": "user_preferences", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_preferences_user_id_unique": { + "name": "user_preferences_user_id_unique", + "nullsNotDistinct": false, + "columns": [ + "user_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.activity_type": { + "name": "activity_type", + "schema": "public", + "values": [ + "CREATE_LIST", + "ADD_ITEM", + "DELETE_ITEM", + "LIKE_REVIEW", + "LIKE_REPLY", + "LIKE_LIST", + "CREATE_REVIEW", + "CREATE_REPLY", + "FOLLOW_USER", + "WATCH_EPISODE", + "CHANGE_STATUS", + "CREATE_ACCOUNT" + ] + }, + "public.import_item_status": { + "name": "import_item_status", + "schema": "public", + "values": [ + "COMPLETED", + "FAILED", + "NOT_STARTED" + ] + }, + "public.import_status_enum": { + "name": "import_status_enum", + "schema": "public", + "values": [ + "PARTIAL", + "COMPLETED", + "FAILED", + "NOT_STARTED" + ] + }, + "public.languages": { + "name": "languages", + "schema": "public", + "values": [ + "en-US", + "es-ES", + "fr-FR", + "it-IT", + "de-DE", + "pt-BR", + "ja-JP" + ] + }, + "public.like_entity": { + "name": "like_entity", + "schema": "public", + "values": [ + "REVIEW", + "REPLY", + "LIST" + ] + }, + "public.list_visibility": { + "name": "list_visibility", + "schema": "public", + "values": [ + "PUBLIC", + "NETWORK", + "PRIVATE" + ] + }, + "public.media_type": { + "name": "media_type", + "schema": "public", + "values": [ + "TV_SHOW", + "MOVIE" + ] + }, + "public.providers_enum": { + "name": "providers_enum", + "schema": "public", + "values": [ + "MY_ANIME_LIST", + "LETTERBOXD" + ] + }, + "public.social_platforms": { + "name": "social_platforms", + "schema": "public", + "values": [ + "INSTAGRAM", + "TIKTOK", + "YOUTUBE", + "X" + ] + }, + "public.status": { + "name": "status", + "schema": "public", + "values": [ + "WATCHLIST", + "WATCHED", + "WATCHING", + "DROPPED" + ] + }, + "public.subscription_status": { + "name": "subscription_status", + "schema": "public", + "values": [ + "ACTIVE", + "CANCELED", + "EXPIRED", + "PENDING_CANCELLATION" + ] + }, + "public.subscription_type": { + "name": "subscription_type", + "schema": "public", + "values": [ + "MEMBER", + "PRO" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/apps/backend/src/db/migrations/meta/_journal.json b/apps/backend/src/db/migrations/meta/_journal.json index 86a7677c..24d0e984 100644 --- a/apps/backend/src/db/migrations/meta/_journal.json +++ b/apps/backend/src/db/migrations/meta/_journal.json @@ -400,6 +400,13 @@ "when": 1770825922905, "tag": "20260211160522_greedy_ricochet", "breakpoints": true + }, + { + "idx": 57, + "version": "7", + "when": 1770955541416, + "tag": "20260213040541_create_indexes", + "breakpoints": true } ] } \ No newline at end of file diff --git a/apps/backend/src/db/schema/index.ts b/apps/backend/src/db/schema/index.ts index 2c39205a..19d93ee6 100644 --- a/apps/backend/src/db/schema/index.ts +++ b/apps/backend/src/db/schema/index.ts @@ -93,6 +93,8 @@ export const followers = pgTable( pk: primaryKey({ columns: [table.followedId, table.followerId], }), + followerIdIdx: index('idx_followers_follower_id').on(table.followerId), + followedIdIdx: index('idx_followers_followed_id').on(table.followedId), } } ) @@ -198,23 +200,32 @@ export const reviewsRepliesRelations = relations(reviewReplies, ({ one }) => ({ }), })) -export const reviews = pgTable('reviews', { - id: uuid('id') - .$defaultFn(() => randomUUID()) - .primaryKey(), - createdAt: timestamp('created_at').defaultNow().notNull(), - userId: uuid('user_id') - .references(() => users.id, { onDelete: 'cascade' }) - .notNull(), - tmdbId: integer('tmdb_id').notNull(), - mediaType: mediaTypeEnum('media_type').notNull(), - review: varchar('review').notNull(), - rating: real('rating').notNull(), - hasSpoilers: boolean('has_spoilers').notNull().default(false), - language: languagesEnum('language'), - seasonNumber: integer('season_number'), - episodeNumber: integer('episode_number'), -}) +export const reviews = pgTable( + 'reviews', + { + id: uuid('id') + .$defaultFn(() => randomUUID()) + .primaryKey(), + createdAt: timestamp('created_at').defaultNow().notNull(), + userId: uuid('user_id') + .references(() => users.id, { onDelete: 'cascade' }) + .notNull(), + tmdbId: integer('tmdb_id').notNull(), + mediaType: mediaTypeEnum('media_type').notNull(), + review: varchar('review').notNull(), + rating: real('rating').notNull(), + hasSpoilers: boolean('has_spoilers').notNull().default(false), + language: languagesEnum('language'), + seasonNumber: integer('season_number'), + episodeNumber: integer('episode_number'), + }, + table => ({ + userRatingIdx: index('idx_reviews_user_rating').on( + table.userId, + table.rating + ), + }) +) export const reviewsRelations = relations(reviews, ({ one }) => ({ users: one(users, { @@ -283,6 +294,15 @@ export const userItems = pgTable( userItems.tmdbId, userItems.mediaType ), + userStatusIdx: index('idx_user_items_user_status').on( + userItems.userId, + userItems.status + ), + userMediaStatusIdx: index('idx_user_items_user_media_status').on( + userItems.userId, + userItems.mediaType, + userItems.status + ), }) ) @@ -408,6 +428,11 @@ export const userEpisodes = pgTable( table.seasonNumber, table.episodeNumber ), + userIdIdx: index('idx_user_episodes_user_id').on(table.userId), + userTmdbIdx: index('idx_user_episodes_user_tmdb').on( + table.userId, + table.tmdbId + ), } } )