Stage M5 and Stage M6
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "OrderStatus" AS ENUM ('PENDING', 'CONFIRMED', 'FULFILLED', 'COMPLETED', 'CANCELLED');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "PaymentStatus" AS ENUM ('UNPAID', 'PAID', 'PARTIALLY_REFUNDED', 'REFUNDED');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "FulfillmentStatus" AS ENUM ('UNFULFILLED', 'PARTIALLY_FULFILLED', 'FULFILLED');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "orders" (
|
||||
"id" UUID NOT NULL,
|
||||
"number" SERIAL NOT NULL,
|
||||
"customer_id" UUID,
|
||||
"email" VARCHAR(255) NOT NULL,
|
||||
"phone" VARCHAR(20) NOT NULL,
|
||||
"status" "OrderStatus" NOT NULL DEFAULT 'PENDING',
|
||||
"payment_status" "PaymentStatus" NOT NULL DEFAULT 'UNPAID',
|
||||
"fulfillment_status" "FulfillmentStatus" NOT NULL DEFAULT 'UNFULFILLED',
|
||||
"currency" "Currency" NOT NULL DEFAULT 'VND',
|
||||
"subtotal_amount" INTEGER NOT NULL,
|
||||
"discount_amount" INTEGER NOT NULL DEFAULT 0,
|
||||
"shipping_amount" INTEGER NOT NULL DEFAULT 0,
|
||||
"tax_amount" INTEGER NOT NULL DEFAULT 0,
|
||||
"total_amount" INTEGER NOT NULL,
|
||||
"ship_full_name" VARCHAR(160) NOT NULL,
|
||||
"ship_phone" VARCHAR(20) NOT NULL,
|
||||
"ship_line1" VARCHAR(255) NOT NULL,
|
||||
"ship_line2" VARCHAR(255),
|
||||
"ship_ward" VARCHAR(120),
|
||||
"ship_district" VARCHAR(120),
|
||||
"ship_province" VARCHAR(120) NOT NULL,
|
||||
"ship_country_code" CHAR(2) NOT NULL DEFAULT 'VN',
|
||||
"ship_postal_code" VARCHAR(20),
|
||||
"customer_note" VARCHAR(1000),
|
||||
"placed_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"confirmed_at" TIMESTAMPTZ(3),
|
||||
"cancelled_at" TIMESTAMPTZ(3),
|
||||
"cancel_reason" VARCHAR(500),
|
||||
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMPTZ(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "orders_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "order_lines" (
|
||||
"id" UUID NOT NULL,
|
||||
"order_id" UUID NOT NULL,
|
||||
"variant_id" UUID,
|
||||
"product_name" VARCHAR(255) NOT NULL,
|
||||
"variant_title" VARCHAR(255) NOT NULL,
|
||||
"sku" VARCHAR(64) NOT NULL,
|
||||
"image_url" VARCHAR(500),
|
||||
"unit_amount" INTEGER NOT NULL,
|
||||
"quantity" INTEGER NOT NULL,
|
||||
"line_amount" INTEGER NOT NULL,
|
||||
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "order_lines_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "orders_number_key" ON "orders"("number");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "orders_customer_id_idx" ON "orders"("customer_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "orders_email_idx" ON "orders"("email");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "orders_status_placed_at_idx" ON "orders"("status", "placed_at");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "order_lines_order_id_idx" ON "order_lines"("order_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "order_lines_variant_id_idx" ON "order_lines"("variant_id");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "orders" ADD CONSTRAINT "orders_customer_id_fkey" FOREIGN KEY ("customer_id") REFERENCES "customers"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "order_lines" ADD CONSTRAINT "order_lines_order_id_fkey" FOREIGN KEY ("order_id") REFERENCES "orders"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "order_lines" ADD CONSTRAINT "order_lines_variant_id_fkey" FOREIGN KEY ("variant_id") REFERENCES "product_variants"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,64 @@
|
||||
-- Full-text search over the catalog, in PostgreSQL (ADR-0012).
|
||||
--
|
||||
-- Two extensions do the work that a dedicated search engine would otherwise be
|
||||
-- brought in for:
|
||||
-- unaccent — so "ao chay bo" finds "Áo Chạy Bộ". Vietnamese shoppers type
|
||||
-- without diacritics constantly; without this, most of them find
|
||||
-- nothing.
|
||||
-- pg_trgm — trigram similarity, which gives typo tolerance and partial-word
|
||||
-- matching that a tsquery alone cannot ("nocturn", "jacket run").
|
||||
CREATE EXTENSION IF NOT EXISTS unaccent;
|
||||
CREATE EXTENSION IF NOT EXISTS pg_trgm;
|
||||
|
||||
-- `unaccent()` is STABLE, not IMMUTABLE, because it depends on a dictionary that
|
||||
-- could in principle be changed. Postgres therefore refuses it in a generated
|
||||
-- column or an index. Pinning the dictionary by name makes the result genuinely
|
||||
-- immutable, which is the standard way around this.
|
||||
CREATE OR REPLACE FUNCTION immutable_unaccent(text)
|
||||
RETURNS text
|
||||
LANGUAGE sql
|
||||
IMMUTABLE
|
||||
STRICT
|
||||
PARALLEL SAFE
|
||||
AS $$
|
||||
SELECT public.unaccent('public.unaccent'::regdictionary, $1)
|
||||
$$;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "search_documents" (
|
||||
"product_id" UUID NOT NULL,
|
||||
"locale" "Locale" NOT NULL,
|
||||
"title" VARCHAR(255) NOT NULL,
|
||||
"keywords" TEXT NOT NULL,
|
||||
"body" TEXT NOT NULL,
|
||||
"updated_at" TIMESTAMPTZ(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "search_documents_pkey" PRIMARY KEY ("product_id","locale")
|
||||
);
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "search_documents" ADD CONSTRAINT "search_documents_product_id_fkey" FOREIGN KEY ("product_id") REFERENCES "products"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- The searchable vector is GENERATED, not written by the application.
|
||||
--
|
||||
-- That is the whole point: an index maintained by hand drifts from the text it
|
||||
-- indexes the first time someone updates one and forgets the other. Here it
|
||||
-- cannot — the database recomputes it on every write to the source columns.
|
||||
--
|
||||
-- `simple` rather than `english`: the catalog is bilingual, and English
|
||||
-- stemming applied to Vietnamese produces nonsense. Weighting carries the
|
||||
-- relevance instead of stemming.
|
||||
ALTER TABLE "search_documents"
|
||||
ADD COLUMN "document" tsvector
|
||||
GENERATED ALWAYS AS (
|
||||
setweight(to_tsvector('simple', immutable_unaccent(coalesce("title", ''))), 'A') ||
|
||||
setweight(to_tsvector('simple', immutable_unaccent(coalesce("keywords", ''))), 'B') ||
|
||||
setweight(to_tsvector('simple', immutable_unaccent(coalesce("body", ''))), 'C')
|
||||
) STORED;
|
||||
|
||||
CREATE INDEX "search_documents_document_idx" ON "search_documents" USING GIN ("document");
|
||||
|
||||
-- Trigram index over the high-signal text only. Including `body` would bloat it
|
||||
-- for matches nobody wants ranked by similarity anyway.
|
||||
CREATE INDEX "search_documents_trgm_idx" ON "search_documents"
|
||||
USING GIN ((immutable_unaccent("title") || ' ' || immutable_unaccent("keywords")) gin_trgm_ops);
|
||||
Reference in New Issue
Block a user