diff --git a/drizzle/0000_gorgeous_puma.sql b/drizzle/0000_gorgeous_puma.sql new file mode 100644 index 0000000..1ce310b --- /dev/null +++ b/drizzle/0000_gorgeous_puma.sql @@ -0,0 +1,59 @@ +CREATE TYPE "public"."comparison_status" AS ENUM('researching', 'completed', 'failed');--> statement-breakpoint +CREATE TABLE "comparison_dimensions" ( + "id" text PRIMARY KEY NOT NULL, + "comparison_id" text NOT NULL, + "name" text NOT NULL, + "description" text, + "weight" integer DEFAULT 1 NOT NULL, + "order" integer DEFAULT 0 NOT NULL +); +--> statement-breakpoint +CREATE TABLE "comparison_items" ( + "id" text PRIMARY KEY NOT NULL, + "comparison_id" text NOT NULL, + "name" text NOT NULL, + "description" text, + "image_url" text, + "research_data" jsonb, + "scores" jsonb, + "pros" text[], + "cons" text[], + "order" integer DEFAULT 0 NOT NULL +); +--> statement-breakpoint +CREATE TABLE "comparisons" ( + "id" text PRIMARY KEY NOT NULL, + "user_id" text NOT NULL, + "title" text NOT NULL, + "query" text NOT NULL, + "slug" text NOT NULL, + "status" "comparison_status" DEFAULT 'researching' NOT NULL, + "summary" text, + "overall_data" jsonb, + "tags" text[], + "is_public" boolean DEFAULT true NOT NULL, + "view_count" integer DEFAULT 0 NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT "comparisons_slug_unique" UNIQUE("slug") +); +--> statement-breakpoint +CREATE TABLE "users" ( + "id" text PRIMARY KEY NOT NULL, + "name" text, + "email" text NOT NULL, + "email_verified" timestamp with time zone, + "image" text, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT "users_email_unique" UNIQUE("email") +); +--> statement-breakpoint +ALTER TABLE "comparison_dimensions" ADD CONSTRAINT "comparison_dimensions_comparison_id_comparisons_id_fk" FOREIGN KEY ("comparison_id") REFERENCES "public"."comparisons"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "comparison_items" ADD CONSTRAINT "comparison_items_comparison_id_comparisons_id_fk" FOREIGN KEY ("comparison_id") REFERENCES "public"."comparisons"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "comparisons" ADD CONSTRAINT "comparisons_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +CREATE INDEX "comparison_dimensions_comparison_id_idx" ON "comparison_dimensions" USING btree ("comparison_id");--> statement-breakpoint +CREATE INDEX "comparison_items_comparison_id_idx" ON "comparison_items" USING btree ("comparison_id");--> statement-breakpoint +CREATE INDEX "comparisons_user_id_idx" ON "comparisons" USING btree ("user_id");--> statement-breakpoint +CREATE INDEX "comparisons_slug_idx" ON "comparisons" USING btree ("slug");--> statement-breakpoint +CREATE INDEX "comparisons_status_idx" ON "comparisons" USING btree ("status"); \ No newline at end of file diff --git a/drizzle/0000_opposite_doomsday.sql b/drizzle/0000_opposite_doomsday.sql deleted file mode 100644 index aa188a6..0000000 --- a/drizzle/0000_opposite_doomsday.sql +++ /dev/null @@ -1,42 +0,0 @@ -CREATE TABLE "comparison_dimensions" ( - "id" text PRIMARY KEY NOT NULL, - "comparison_id" text NOT NULL, - "name" text NOT NULL, - "description" text, - "weight" integer DEFAULT 1, - "order" integer NOT NULL -); ---> statement-breakpoint -CREATE TABLE "comparison_items" ( - "id" text PRIMARY KEY NOT NULL, - "comparison_id" text NOT NULL, - "name" text NOT NULL, - "description" text, - "image_url" text, - "research_data" jsonb, - "scores" jsonb, - "pros" text[], - "cons" text[], - "order" integer NOT NULL -); ---> statement-breakpoint -CREATE TABLE "comparisons" ( - "id" text PRIMARY KEY NOT NULL, - "user_id" text, - "title" text NOT NULL, - "query" text, - "slug" varchar(255) NOT NULL, - "status" text DEFAULT 'researching' NOT NULL, - "summary" text, - "overall_data" jsonb, - "tags" text[], - "is_public" boolean DEFAULT false, - "view_count" integer DEFAULT 0, - "created_at" timestamp DEFAULT now() NOT NULL, - "updated_at" timestamp DEFAULT now() NOT NULL, - CONSTRAINT "comparisons_slug_unique" UNIQUE("slug") -); ---> statement-breakpoint -ALTER TABLE "comparison_dimensions" ADD CONSTRAINT "comparison_dimensions_comparison_id_comparisons_id_fk" FOREIGN KEY ("comparison_id") REFERENCES "public"."comparisons"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "comparison_items" ADD CONSTRAINT "comparison_items_comparison_id_comparisons_id_fk" FOREIGN KEY ("comparison_id") REFERENCES "public"."comparisons"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -CREATE INDEX "comparisons_user_id_idx" ON "comparisons" USING btree ("user_id"); \ No newline at end of file diff --git a/drizzle/meta/0000_snapshot.json b/drizzle/meta/0000_snapshot.json index a84e653..5a29fe5 100644 --- a/drizzle/meta/0000_snapshot.json +++ b/drizzle/meta/0000_snapshot.json @@ -1,5 +1,5 @@ { - "id": "6cc67b11-8016-409b-9de9-8966593c97b0", + "id": "c719fbf4-6ed1-4b38-9a09-33a7e0799267", "prevId": "00000000-0000-0000-0000-000000000000", "version": "7", "dialect": "postgresql", @@ -36,17 +36,34 @@ "name": "weight", "type": "integer", "primaryKey": false, - "notNull": false, + "notNull": true, "default": 1 }, "order": { "name": "order", "type": "integer", "primaryKey": false, - "notNull": true + "notNull": true, + "default": 0 + } + }, + "indexes": { + "comparison_dimensions_comparison_id_idx": { + "name": "comparison_dimensions_comparison_id_idx", + "columns": [ + { + "expression": "comparison_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} } }, - "indexes": {}, "foreignKeys": { "comparison_dimensions_comparison_id_comparisons_id_fk": { "name": "comparison_dimensions_comparison_id_comparisons_id_fk", @@ -130,10 +147,27 @@ "name": "order", "type": "integer", "primaryKey": false, - "notNull": true + "notNull": true, + "default": 0 + } + }, + "indexes": { + "comparison_items_comparison_id_idx": { + "name": "comparison_items_comparison_id_idx", + "columns": [ + { + "expression": "comparison_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} } }, - "indexes": {}, "foreignKeys": { "comparison_items_comparison_id_comparisons_id_fk": { "name": "comparison_items_comparison_id_comparisons_id_fk", @@ -169,7 +203,7 @@ "name": "user_id", "type": "text", "primaryKey": false, - "notNull": false + "notNull": true }, "title": { "name": "title", @@ -181,17 +215,18 @@ "name": "query", "type": "text", "primaryKey": false, - "notNull": false + "notNull": true }, "slug": { "name": "slug", - "type": "varchar(255)", + "type": "text", "primaryKey": false, "notNull": true }, "status": { "name": "status", - "type": "text", + "type": "comparison_status", + "typeSchema": "public", "primaryKey": false, "notNull": true, "default": "'researching'" @@ -218,26 +253,26 @@ "name": "is_public", "type": "boolean", "primaryKey": false, - "notNull": false, - "default": false + "notNull": true, + "default": true }, "view_count": { "name": "view_count", "type": "integer", "primaryKey": false, - "notNull": false, + "notNull": true, "default": 0 }, "created_at": { "name": "created_at", - "type": "timestamp", + "type": "timestamp with time zone", "primaryKey": false, "notNull": true, "default": "now()" }, "updated_at": { "name": "updated_at", - "type": "timestamp", + "type": "timestamp with time zone", "primaryKey": false, "notNull": true, "default": "now()" @@ -258,9 +293,53 @@ "concurrently": false, "method": "btree", "with": {} + }, + "comparisons_slug_idx": { + "name": "comparisons_slug_idx", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "comparisons_status_idx": { + "name": "comparisons_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "comparisons_user_id_users_id_fk": { + "name": "comparisons_user_id_users_id_fk", + "tableFrom": "comparisons", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" } }, - "foreignKeys": {}, "compositePrimaryKeys": {}, "uniqueConstraints": { "comparisons_slug_unique": { @@ -274,9 +353,84 @@ "policies": {}, "checkConstraints": {}, "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_email_unique": { + "name": "users_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.comparison_status": { + "name": "comparison_status", + "schema": "public", + "values": [ + "researching", + "completed", + "failed" + ] } }, - "enums": {}, "schemas": {}, "sequences": {}, "roles": {}, diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index 850ca4a..dec115a 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -5,8 +5,8 @@ { "idx": 0, "version": "7", - "when": 1777066133958, - "tag": "0000_opposite_doomsday", + "when": 1777066297133, + "tag": "0000_gorgeous_puma", "breakpoints": true } ] diff --git a/src/lib/db/schema.ts b/src/lib/db/schema.ts index 33407e4..d37eeb0 100644 --- a/src/lib/db/schema.ts +++ b/src/lib/db/schema.ts @@ -1,13 +1,30 @@ import { + boolean, + index, + integer, + jsonb, + pgEnum, pgTable, text, timestamp, - jsonb, - integer, - boolean, - varchar, - index, } from "drizzle-orm/pg-core"; +import { createId } from "@paralleldrive/cuid2"; + +export const users = pgTable("users", { + id: text("id").primaryKey(), + name: text("name"), + email: text("email").notNull().unique(), + emailVerified: timestamp("email_verified", { withTimezone: true }), + image: text("image"), + createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), + updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(), +}); + +export const comparisonStatusEnum = pgEnum("comparison_status", [ + "researching", + "completed", + "failed", +]); export const users = pgTable("users", { id: text("id").primaryKey(), @@ -33,55 +50,65 @@ export const sessions = pgTable("sessions", { export const comparisons = pgTable( "comparisons", { - id: text("id").primaryKey(), - userId: text("user_id"), - title: text("title").notNull(), - query: text("query"), - slug: varchar("slug", { length: 255 }).notNull().unique(), - status: text("status", { - enum: ["researching", "completed", "failed"], - }) + id: text("id") + .primaryKey() + .$defaultFn(() => createId()), + userId: text("user_id") .notNull() - .default("researching"), + .references(() => users.id), + title: text("title").notNull(), + query: text("query").notNull(), + slug: text("slug").notNull().unique(), + status: comparisonStatusEnum("status").notNull().default("researching"), summary: text("summary"), overallData: jsonb("overall_data"), tags: text("tags").array(), - isPublic: boolean("is_public").default(false), - viewCount: integer("view_count").default(0), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().notNull(), + isPublic: boolean("is_public").notNull().default(true), + viewCount: integer("view_count").notNull().default(0), + createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), + updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(), }, - (table) => [index("comparisons_user_id_idx").on(table.userId)] + (table) => [ + index("comparisons_user_id_idx").on(table.userId), + index("comparisons_slug_idx").on(table.slug), + index("comparisons_status_idx").on(table.status), + ], ); -export const comparisonItems = pgTable("comparison_items", { - id: text("id").primaryKey(), - comparisonId: text("comparison_id") - .notNull() - .references(() => comparisons.id, { onDelete: "cascade" }), - name: text("name").notNull(), - description: text("description"), - imageUrl: text("image_url"), - researchData: jsonb("research_data"), - scores: jsonb("scores"), - pros: text("pros").array(), - cons: text("cons").array(), - order: integer("order").notNull(), -}); +export const comparisonItems = pgTable( + "comparison_items", + { + id: text("id") + .primaryKey() + .$defaultFn(() => createId()), + comparisonId: text("comparison_id") + .notNull() + .references(() => comparisons.id, { onDelete: "cascade" }), + name: text("name").notNull(), + description: text("description"), + imageUrl: text("image_url"), + researchData: jsonb("research_data"), + scores: jsonb("scores"), + pros: text("pros").array(), + cons: text("cons").array(), + order: integer("order").notNull().default(0), + }, + (table) => [index("comparison_items_comparison_id_idx").on(table.comparisonId)], +); -export const comparisonDimensions = pgTable("comparison_dimensions", { - id: text("id").primaryKey(), - comparisonId: text("comparison_id") - .notNull() - .references(() => comparisons.id, { onDelete: "cascade" }), - name: text("name").notNull(), - description: text("description"), - weight: integer("weight").default(1), - order: integer("order").notNull(), -}); - -export type Comparison = typeof comparisons.$inferSelect; -export type NewComparison = typeof comparisons.$inferInsert; -export type ComparisonItem = typeof comparisonItems.$inferSelect; -export type NewComparisonItem = typeof comparisonItems.$inferInsert; -export type ComparisonDimension = typeof comparisonDimensions.$inferSelect; +export const comparisonDimensions = pgTable( + "comparison_dimensions", + { + id: text("id") + .primaryKey() + .$defaultFn(() => createId()), + comparisonId: text("comparison_id") + .notNull() + .references(() => comparisons.id, { onDelete: "cascade" }), + name: text("name").notNull(), + description: text("description"), + weight: integer("weight").notNull().default(1), + order: integer("order").notNull().default(0), + }, + (table) => [index("comparison_dimensions_comparison_id_idx").on(table.comparisonId)], +);