Fold all incremental schema changes into the baseline migration so that a fresh database only needs to run one file: - recipe_source enum now includes 'menu' - recipe_products/recipe_product_translations renamed to recipe_ingredients/recipe_ingredient_translations; product_id → ingredient_id - menu_items.meal_type CHECK expanded to all 6 types (breakfast, second_breakfast, lunch, afternoon_snack, dinner, snack) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
654 lines
29 KiB
PL/PgSQL
654 lines
29 KiB
PL/PgSQL
-- +goose Up
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- Extensions
|
||
-- ---------------------------------------------------------------------------
|
||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||
CREATE EXTENSION IF NOT EXISTS pg_trgm;
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- UUID v7 generator (time-ordered, millisecond precision).
|
||
-- Structure: 48-bit unix_ts_ms | 4-bit version (0111) | 12-bit rand_a | 2-bit variant (10) | 62-bit rand_b
|
||
-- ---------------------------------------------------------------------------
|
||
-- +goose StatementBegin
|
||
CREATE OR REPLACE FUNCTION uuid_generate_v7()
|
||
RETURNS uuid
|
||
AS $$
|
||
DECLARE
|
||
unix_ts_ms bytea;
|
||
uuid_bytes bytea;
|
||
BEGIN
|
||
-- 48-bit Unix timestamp in milliseconds.
|
||
-- int8send produces 8 big-endian bytes; skip the first 2 zero bytes to get 6.
|
||
unix_ts_ms = substring(int8send(floor(extract(epoch from clock_timestamp()) * 1000)::bigint) from 3);
|
||
|
||
-- Use a random v4 UUID as the source of random bits for rand_a and rand_b.
|
||
uuid_bytes = uuid_send(gen_random_uuid());
|
||
|
||
-- Overwrite bytes 0-5 with the timestamp (positions 1-6 in 1-indexed bytea).
|
||
uuid_bytes = overlay(uuid_bytes placing unix_ts_ms from 1 for 6);
|
||
|
||
-- Set version nibble (bits 48-51) to 0111 (7).
|
||
uuid_bytes = set_bit(uuid_bytes, 48, 0);
|
||
uuid_bytes = set_bit(uuid_bytes, 49, 1);
|
||
uuid_bytes = set_bit(uuid_bytes, 50, 1);
|
||
uuid_bytes = set_bit(uuid_bytes, 51, 1);
|
||
|
||
-- Variant bits (64-65) stay at 10 as inherited from gen_random_uuid().
|
||
|
||
RETURN encode(uuid_bytes, 'hex')::uuid;
|
||
END
|
||
$$ LANGUAGE plpgsql VOLATILE;
|
||
-- +goose StatementEnd
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- Enums
|
||
-- ---------------------------------------------------------------------------
|
||
CREATE TYPE user_plan AS ENUM ('free', 'paid');
|
||
CREATE TYPE user_gender AS ENUM ('male', 'female');
|
||
CREATE TYPE user_goal AS ENUM ('lose', 'maintain', 'gain');
|
||
CREATE TYPE activity_level AS ENUM ('low', 'moderate', 'high');
|
||
CREATE TYPE recipe_source AS ENUM ('spoonacular', 'ai', 'user', 'menu');
|
||
CREATE TYPE recipe_difficulty AS ENUM ('easy', 'medium', 'hard');
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- users
|
||
-- ---------------------------------------------------------------------------
|
||
CREATE TABLE users (
|
||
id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
|
||
firebase_uid VARCHAR(128) NOT NULL UNIQUE,
|
||
email VARCHAR(255) NOT NULL,
|
||
name VARCHAR(255) NOT NULL DEFAULT '',
|
||
avatar_url TEXT,
|
||
height_cm SMALLINT,
|
||
weight_kg DECIMAL(5,2),
|
||
date_of_birth DATE,
|
||
gender user_gender,
|
||
activity activity_level,
|
||
goal user_goal,
|
||
daily_calories INTEGER,
|
||
plan user_plan NOT NULL DEFAULT 'free',
|
||
preferences JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||
refresh_token TEXT,
|
||
token_expires_at TIMESTAMPTZ,
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||
);
|
||
CREATE INDEX idx_users_firebase_uid ON users (firebase_uid);
|
||
CREATE INDEX idx_users_email ON users (email);
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- languages
|
||
-- ---------------------------------------------------------------------------
|
||
CREATE TABLE languages (
|
||
code VARCHAR(10) PRIMARY KEY,
|
||
native_name TEXT NOT NULL,
|
||
english_name TEXT NOT NULL,
|
||
is_active BOOLEAN NOT NULL DEFAULT true,
|
||
sort_order SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- units + unit_translations
|
||
-- ---------------------------------------------------------------------------
|
||
CREATE TABLE units (
|
||
code VARCHAR(20) PRIMARY KEY,
|
||
sort_order SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
|
||
CREATE TABLE unit_translations (
|
||
unit_code VARCHAR(20) NOT NULL REFERENCES units(code) ON DELETE CASCADE,
|
||
lang VARCHAR(10) NOT NULL,
|
||
name TEXT NOT NULL,
|
||
PRIMARY KEY (unit_code, lang)
|
||
);
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- product_categories + product_category_translations
|
||
-- ---------------------------------------------------------------------------
|
||
CREATE TABLE product_categories (
|
||
slug VARCHAR(50) PRIMARY KEY,
|
||
name TEXT NOT NULL,
|
||
sort_order SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
|
||
CREATE TABLE product_category_translations (
|
||
product_category_slug VARCHAR(50) NOT NULL REFERENCES product_categories(slug) ON DELETE CASCADE,
|
||
lang VARCHAR(10) NOT NULL,
|
||
name TEXT NOT NULL,
|
||
PRIMARY KEY (product_category_slug, lang)
|
||
);
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- products (canonical catalog)
|
||
-- ---------------------------------------------------------------------------
|
||
CREATE TABLE products (
|
||
id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
|
||
canonical_name TEXT NOT NULL,
|
||
category VARCHAR(50) REFERENCES product_categories(slug),
|
||
default_unit VARCHAR(20) REFERENCES units(code),
|
||
barcode TEXT UNIQUE,
|
||
calories_per_100g DECIMAL(8,2),
|
||
protein_per_100g DECIMAL(8,2),
|
||
fat_per_100g DECIMAL(8,2),
|
||
carbs_per_100g DECIMAL(8,2),
|
||
fiber_per_100g DECIMAL(8,2),
|
||
storage_days INTEGER,
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
CONSTRAINT uq_product_canonical_name UNIQUE (canonical_name)
|
||
);
|
||
CREATE INDEX idx_products_canonical_name ON products (canonical_name);
|
||
CREATE INDEX idx_products_category ON products (category);
|
||
CREATE INDEX idx_products_barcode ON products (barcode) WHERE barcode IS NOT NULL;
|
||
CREATE INDEX idx_products_fts ON products USING GIN (to_tsvector('english', canonical_name));
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- product_aliases
|
||
-- ---------------------------------------------------------------------------
|
||
CREATE TABLE product_aliases (
|
||
product_id UUID NOT NULL REFERENCES products(id) ON DELETE CASCADE,
|
||
lang VARCHAR(10) NOT NULL,
|
||
alias TEXT NOT NULL,
|
||
PRIMARY KEY (product_id, lang, alias)
|
||
);
|
||
CREATE INDEX idx_product_aliases_lookup ON product_aliases (product_id, lang);
|
||
CREATE INDEX idx_product_aliases_trgm ON product_aliases USING GIN (alias gin_trgm_ops);
|
||
CREATE INDEX idx_product_aliases_fts ON product_aliases USING GIN (to_tsvector('simple', alias));
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- cuisines + cuisine_translations
|
||
-- ---------------------------------------------------------------------------
|
||
CREATE TABLE cuisines (
|
||
slug VARCHAR(50) PRIMARY KEY,
|
||
name TEXT NOT NULL,
|
||
sort_order SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
|
||
CREATE TABLE cuisine_translations (
|
||
cuisine_slug VARCHAR(50) NOT NULL REFERENCES cuisines(slug) ON DELETE CASCADE,
|
||
lang VARCHAR(10) NOT NULL,
|
||
name TEXT NOT NULL,
|
||
PRIMARY KEY (cuisine_slug, lang)
|
||
);
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- tags + tag_translations
|
||
-- ---------------------------------------------------------------------------
|
||
CREATE TABLE tags (
|
||
slug VARCHAR(100) PRIMARY KEY,
|
||
name TEXT NOT NULL,
|
||
sort_order SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
|
||
CREATE TABLE tag_translations (
|
||
tag_slug VARCHAR(100) NOT NULL REFERENCES tags(slug) ON DELETE CASCADE,
|
||
lang VARCHAR(10) NOT NULL,
|
||
name TEXT NOT NULL,
|
||
PRIMARY KEY (tag_slug, lang)
|
||
);
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- dish_categories + dish_category_translations
|
||
-- ---------------------------------------------------------------------------
|
||
CREATE TABLE dish_categories (
|
||
slug VARCHAR(50) PRIMARY KEY,
|
||
name TEXT NOT NULL,
|
||
sort_order SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
|
||
CREATE TABLE dish_category_translations (
|
||
category_slug VARCHAR(50) NOT NULL REFERENCES dish_categories(slug) ON DELETE CASCADE,
|
||
lang VARCHAR(10) NOT NULL,
|
||
name TEXT NOT NULL,
|
||
PRIMARY KEY (category_slug, lang)
|
||
);
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- dishes + dish_translations + dish_tags
|
||
-- ---------------------------------------------------------------------------
|
||
CREATE TABLE dishes (
|
||
id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
|
||
cuisine_slug VARCHAR(50) REFERENCES cuisines(slug) ON DELETE SET NULL,
|
||
category_slug VARCHAR(50) REFERENCES dish_categories(slug) ON DELETE SET NULL,
|
||
name TEXT NOT NULL,
|
||
description TEXT,
|
||
image_url TEXT,
|
||
avg_rating DECIMAL(3,2) NOT NULL DEFAULT 0.0,
|
||
review_count INTEGER NOT NULL DEFAULT 0,
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||
);
|
||
CREATE INDEX idx_dishes_cuisine ON dishes (cuisine_slug);
|
||
CREATE INDEX idx_dishes_category ON dishes (category_slug);
|
||
CREATE INDEX idx_dishes_rating ON dishes (avg_rating DESC);
|
||
CREATE INDEX idx_dishes_name_fts ON dishes USING GIN (to_tsvector('english', name));
|
||
CREATE INDEX idx_dishes_name_trgm ON dishes USING GIN (name gin_trgm_ops);
|
||
|
||
CREATE TABLE dish_translations (
|
||
dish_id UUID NOT NULL REFERENCES dishes(id) ON DELETE CASCADE,
|
||
lang VARCHAR(10) NOT NULL,
|
||
name TEXT NOT NULL,
|
||
description TEXT,
|
||
PRIMARY KEY (dish_id, lang)
|
||
);
|
||
CREATE INDEX idx_dish_translations_name_fts ON dish_translations USING GIN (to_tsvector('simple', name));
|
||
CREATE INDEX idx_dish_translations_name_trgm ON dish_translations USING GIN (name gin_trgm_ops);
|
||
|
||
CREATE TABLE dish_tags (
|
||
dish_id UUID NOT NULL REFERENCES dishes(id) ON DELETE CASCADE,
|
||
tag_slug VARCHAR(100) NOT NULL REFERENCES tags(slug) ON DELETE CASCADE,
|
||
PRIMARY KEY (dish_id, tag_slug)
|
||
);
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- recipes (one cooking variant of a dish)
|
||
-- ---------------------------------------------------------------------------
|
||
CREATE TABLE recipes (
|
||
id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
|
||
dish_id UUID NOT NULL REFERENCES dishes(id) ON DELETE CASCADE,
|
||
source recipe_source NOT NULL DEFAULT 'ai',
|
||
difficulty recipe_difficulty,
|
||
prep_time_min INTEGER,
|
||
cook_time_min INTEGER,
|
||
servings SMALLINT,
|
||
calories_per_serving DECIMAL(8,2),
|
||
protein_per_serving DECIMAL(8,2),
|
||
fat_per_serving DECIMAL(8,2),
|
||
carbs_per_serving DECIMAL(8,2),
|
||
fiber_per_serving DECIMAL(8,2),
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||
);
|
||
CREATE INDEX idx_recipes_dish_id ON recipes (dish_id);
|
||
CREATE INDEX idx_recipes_difficulty ON recipes (difficulty);
|
||
CREATE INDEX idx_recipes_prep_time ON recipes (prep_time_min);
|
||
CREATE INDEX idx_recipes_calories ON recipes (calories_per_serving);
|
||
CREATE INDEX idx_recipes_source ON recipes (source);
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- recipe_translations
|
||
-- ---------------------------------------------------------------------------
|
||
CREATE TABLE recipe_translations (
|
||
recipe_id UUID NOT NULL REFERENCES recipes(id) ON DELETE CASCADE,
|
||
lang VARCHAR(10) NOT NULL,
|
||
notes TEXT,
|
||
PRIMARY KEY (recipe_id, lang)
|
||
);
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- recipe_ingredients + recipe_ingredient_translations
|
||
-- ---------------------------------------------------------------------------
|
||
CREATE TABLE recipe_ingredients (
|
||
id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
|
||
recipe_id UUID NOT NULL REFERENCES recipes(id) ON DELETE CASCADE,
|
||
ingredient_id UUID REFERENCES products(id) ON DELETE SET NULL,
|
||
name TEXT NOT NULL,
|
||
amount DECIMAL(10,2) NOT NULL DEFAULT 0,
|
||
unit_code VARCHAR(20),
|
||
is_optional BOOLEAN NOT NULL DEFAULT false,
|
||
sort_order SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
CREATE INDEX idx_recipe_ingredients_recipe_id ON recipe_ingredients (recipe_id);
|
||
|
||
CREATE TABLE recipe_ingredient_translations (
|
||
ri_id UUID NOT NULL REFERENCES recipe_ingredients(id) ON DELETE CASCADE,
|
||
lang VARCHAR(10) NOT NULL,
|
||
name TEXT NOT NULL,
|
||
PRIMARY KEY (ri_id, lang)
|
||
);
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- recipe_steps + recipe_step_translations
|
||
-- ---------------------------------------------------------------------------
|
||
CREATE TABLE recipe_steps (
|
||
id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
|
||
recipe_id UUID NOT NULL REFERENCES recipes(id) ON DELETE CASCADE,
|
||
step_number SMALLINT NOT NULL,
|
||
timer_seconds INTEGER,
|
||
image_url TEXT,
|
||
description TEXT NOT NULL,
|
||
UNIQUE (recipe_id, step_number)
|
||
);
|
||
CREATE INDEX idx_recipe_steps_recipe_id ON recipe_steps (recipe_id);
|
||
|
||
CREATE TABLE recipe_step_translations (
|
||
step_id UUID NOT NULL REFERENCES recipe_steps(id) ON DELETE CASCADE,
|
||
lang VARCHAR(10) NOT NULL,
|
||
description TEXT NOT NULL,
|
||
PRIMARY KEY (step_id, lang)
|
||
);
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- user_products (user fridge / pantry items)
|
||
-- ---------------------------------------------------------------------------
|
||
CREATE TABLE user_products (
|
||
id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
|
||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||
primary_product_id UUID REFERENCES products(id),
|
||
name TEXT NOT NULL,
|
||
quantity DECIMAL(10,2) NOT NULL DEFAULT 1,
|
||
unit TEXT NOT NULL DEFAULT 'pcs' REFERENCES units(code),
|
||
category TEXT,
|
||
storage_days INT NOT NULL DEFAULT 7,
|
||
added_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||
);
|
||
CREATE INDEX idx_user_products_user_id ON user_products (user_id);
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- user_product_components (M2M: composite user product ↔ catalog products)
|
||
-- ---------------------------------------------------------------------------
|
||
CREATE TABLE user_product_components (
|
||
user_product_id UUID NOT NULL REFERENCES user_products(id) ON DELETE CASCADE,
|
||
product_id UUID NOT NULL REFERENCES products(id) ON DELETE CASCADE,
|
||
amount_per_100g DECIMAL(10,2),
|
||
PRIMARY KEY (user_product_id, product_id)
|
||
);
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- user_saved_recipes
|
||
-- ---------------------------------------------------------------------------
|
||
CREATE TABLE user_saved_recipes (
|
||
id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
|
||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||
recipe_id UUID NOT NULL REFERENCES recipes(id) ON DELETE CASCADE,
|
||
saved_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
UNIQUE (user_id, recipe_id)
|
||
);
|
||
CREATE INDEX idx_user_saved_recipes_user_id ON user_saved_recipes (user_id, saved_at DESC);
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- menu_plans + menu_items + shopping_lists
|
||
-- ---------------------------------------------------------------------------
|
||
CREATE TABLE menu_plans (
|
||
id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
|
||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||
week_start DATE NOT NULL,
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
UNIQUE (user_id, week_start)
|
||
);
|
||
|
||
CREATE TABLE menu_items (
|
||
id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
|
||
menu_plan_id UUID NOT NULL REFERENCES menu_plans(id) ON DELETE CASCADE,
|
||
day_of_week INT NOT NULL CHECK (day_of_week BETWEEN 1 AND 7),
|
||
meal_type TEXT NOT NULL CHECK (meal_type IN ('breakfast','second_breakfast','lunch','afternoon_snack','dinner','snack')),
|
||
recipe_id UUID REFERENCES recipes(id) ON DELETE SET NULL,
|
||
dish_id UUID REFERENCES dishes(id) ON DELETE SET NULL,
|
||
recipe_data JSONB,
|
||
UNIQUE (menu_plan_id, day_of_week, meal_type)
|
||
);
|
||
|
||
CREATE TABLE shopping_lists (
|
||
id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
|
||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||
menu_plan_id UUID REFERENCES menu_plans(id) ON DELETE CASCADE,
|
||
items JSONB NOT NULL DEFAULT '[]',
|
||
generated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
UNIQUE (user_id, menu_plan_id)
|
||
);
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- dish_recognition_jobs
|
||
-- ---------------------------------------------------------------------------
|
||
CREATE TABLE dish_recognition_jobs (
|
||
id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
|
||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||
user_plan TEXT NOT NULL,
|
||
image_base64 TEXT NOT NULL,
|
||
mime_type TEXT NOT NULL DEFAULT 'image/jpeg',
|
||
lang TEXT NOT NULL DEFAULT 'en',
|
||
target_date DATE,
|
||
target_meal_type TEXT,
|
||
status TEXT NOT NULL DEFAULT 'pending',
|
||
-- pending | processing | done | failed
|
||
result JSONB,
|
||
error TEXT,
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
started_at TIMESTAMPTZ,
|
||
completed_at TIMESTAMPTZ
|
||
);
|
||
CREATE INDEX idx_dish_recognition_jobs_user
|
||
ON dish_recognition_jobs (user_id, created_at DESC);
|
||
CREATE INDEX idx_dish_recognition_jobs_pending
|
||
ON dish_recognition_jobs (status, user_plan, created_at ASC);
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- meal_diary
|
||
-- ---------------------------------------------------------------------------
|
||
CREATE TABLE meal_diary (
|
||
id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
|
||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||
date DATE NOT NULL,
|
||
meal_type TEXT NOT NULL,
|
||
portions DECIMAL(5,2) NOT NULL DEFAULT 1,
|
||
source TEXT NOT NULL DEFAULT 'manual',
|
||
dish_id UUID REFERENCES dishes(id) ON DELETE RESTRICT,
|
||
product_id UUID REFERENCES products(id) ON DELETE RESTRICT,
|
||
recipe_id UUID REFERENCES recipes(id) ON DELETE SET NULL,
|
||
portion_g DECIMAL(10,2),
|
||
job_id UUID REFERENCES dish_recognition_jobs(id) ON DELETE SET NULL,
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
CONSTRAINT chk_meal_entry_source CHECK (num_nonnulls(dish_id, product_id) = 1)
|
||
);
|
||
CREATE INDEX idx_meal_diary_user_date ON meal_diary (user_id, date);
|
||
CREATE INDEX idx_meal_diary_job_id ON meal_diary (job_id) WHERE job_id IS NOT NULL;
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- Seed data: languages
|
||
-- ---------------------------------------------------------------------------
|
||
INSERT INTO languages (code, native_name, english_name, sort_order) VALUES
|
||
('en', 'English', 'English', 1),
|
||
('ru', 'Русский', 'Russian', 2),
|
||
('es', 'Español', 'Spanish', 3),
|
||
('de', 'Deutsch', 'German', 4),
|
||
('fr', 'Français', 'French', 5),
|
||
('it', 'Italiano', 'Italian', 6),
|
||
('pt', 'Português', 'Portuguese', 7),
|
||
('zh', '中文', 'Chinese (Simplified)', 8),
|
||
('ja', '日本語', 'Japanese', 9),
|
||
('ko', '한국어', 'Korean', 10),
|
||
('ar', 'العربية', 'Arabic', 11),
|
||
('hi', 'हिन्दी', 'Hindi', 12);
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- Seed data: units + unit_translations
|
||
-- ---------------------------------------------------------------------------
|
||
INSERT INTO units (code, sort_order) VALUES
|
||
('g', 1),
|
||
('kg', 2),
|
||
('ml', 3),
|
||
('l', 4),
|
||
('pcs', 5),
|
||
('pack', 6);
|
||
|
||
INSERT INTO unit_translations (unit_code, lang, name) VALUES
|
||
('g', 'ru', 'г'),
|
||
('kg', 'ru', 'кг'),
|
||
('ml', 'ru', 'мл'),
|
||
('l', 'ru', 'л'),
|
||
('pcs', 'ru', 'шт'),
|
||
('pack', 'ru', 'уп');
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- Seed data: product_categories + product_category_translations
|
||
-- ---------------------------------------------------------------------------
|
||
INSERT INTO product_categories (slug, name, sort_order) VALUES
|
||
('dairy', 'Dairy', 1),
|
||
('meat', 'Meat', 2),
|
||
('produce', 'Produce', 3),
|
||
('bakery', 'Bakery', 4),
|
||
('frozen', 'Frozen', 5),
|
||
('beverages', 'Beverages', 6),
|
||
('other', 'Other', 7);
|
||
|
||
INSERT INTO product_category_translations (product_category_slug, lang, name) VALUES
|
||
('dairy', 'ru', 'Молочные продукты'),
|
||
('meat', 'ru', 'Мясо и птица'),
|
||
('produce', 'ru', 'Овощи и фрукты'),
|
||
('bakery', 'ru', 'Выпечка и хлеб'),
|
||
('frozen', 'ru', 'Замороженные'),
|
||
('beverages', 'ru', 'Напитки'),
|
||
('other', 'ru', 'Прочее');
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- Seed data: cuisines + cuisine_translations
|
||
-- ---------------------------------------------------------------------------
|
||
INSERT INTO cuisines (slug, name, sort_order) VALUES
|
||
('italian', 'Italian', 1),
|
||
('french', 'French', 2),
|
||
('russian', 'Russian', 3),
|
||
('chinese', 'Chinese', 4),
|
||
('japanese', 'Japanese', 5),
|
||
('korean', 'Korean', 6),
|
||
('mexican', 'Mexican', 7),
|
||
('mediterranean', 'Mediterranean', 8),
|
||
('indian', 'Indian', 9),
|
||
('thai', 'Thai', 10),
|
||
('american', 'American', 11),
|
||
('georgian', 'Georgian', 12),
|
||
('spanish', 'Spanish', 13),
|
||
('german', 'German', 14),
|
||
('middle_eastern', 'Middle Eastern', 15),
|
||
('turkish', 'Turkish', 16),
|
||
('greek', 'Greek', 17),
|
||
('vietnamese', 'Vietnamese', 18),
|
||
('other', 'Other', 19);
|
||
|
||
INSERT INTO cuisine_translations (cuisine_slug, lang, name) VALUES
|
||
('italian', 'ru', 'Итальянская'),
|
||
('french', 'ru', 'Французская'),
|
||
('russian', 'ru', 'Русская'),
|
||
('chinese', 'ru', 'Китайская'),
|
||
('japanese', 'ru', 'Японская'),
|
||
('korean', 'ru', 'Корейская'),
|
||
('mexican', 'ru', 'Мексиканская'),
|
||
('mediterranean', 'ru', 'Средиземноморская'),
|
||
('indian', 'ru', 'Индийская'),
|
||
('thai', 'ru', 'Тайская'),
|
||
('american', 'ru', 'Американская'),
|
||
('georgian', 'ru', 'Грузинская'),
|
||
('spanish', 'ru', 'Испанская'),
|
||
('german', 'ru', 'Немецкая'),
|
||
('middle_eastern', 'ru', 'Ближневосточная'),
|
||
('turkish', 'ru', 'Турецкая'),
|
||
('greek', 'ru', 'Греческая'),
|
||
('vietnamese', 'ru', 'Вьетнамская'),
|
||
('other', 'ru', 'Другая');
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- Seed data: tags + tag_translations
|
||
-- ---------------------------------------------------------------------------
|
||
INSERT INTO tags (slug, name, sort_order) VALUES
|
||
('vegan', 'Vegan', 1),
|
||
('vegetarian', 'Vegetarian', 2),
|
||
('gluten_free', 'Gluten-Free', 3),
|
||
('dairy_free', 'Dairy-Free', 4),
|
||
('healthy', 'Healthy', 5),
|
||
('quick', 'Quick', 6),
|
||
('spicy', 'Spicy', 7),
|
||
('sweet', 'Sweet', 8),
|
||
('soup', 'Soup', 9),
|
||
('salad', 'Salad', 10),
|
||
('main_course', 'Main Course', 11),
|
||
('appetizer', 'Appetizer', 12),
|
||
('breakfast', 'Breakfast', 13),
|
||
('dessert', 'Dessert', 14),
|
||
('grilled', 'Grilled', 15),
|
||
('baked', 'Baked', 16),
|
||
('fried', 'Fried', 17),
|
||
('raw', 'Raw', 18),
|
||
('fermented', 'Fermented', 19);
|
||
|
||
INSERT INTO tag_translations (tag_slug, lang, name) VALUES
|
||
('vegan', 'ru', 'Веганское'),
|
||
('vegetarian', 'ru', 'Вегетарианское'),
|
||
('gluten_free', 'ru', 'Без глютена'),
|
||
('dairy_free', 'ru', 'Без молока'),
|
||
('healthy', 'ru', 'Здоровое'),
|
||
('quick', 'ru', 'Быстрое'),
|
||
('spicy', 'ru', 'Острое'),
|
||
('sweet', 'ru', 'Сладкое'),
|
||
('soup', 'ru', 'Суп'),
|
||
('salad', 'ru', 'Салат'),
|
||
('main_course', 'ru', 'Основное блюдо'),
|
||
('appetizer', 'ru', 'Закуска'),
|
||
('breakfast', 'ru', 'Завтрак'),
|
||
('dessert', 'ru', 'Десерт'),
|
||
('grilled', 'ru', 'Жареное на гриле'),
|
||
('baked', 'ru', 'Запечённое'),
|
||
('fried', 'ru', 'Жареное'),
|
||
('raw', 'ru', 'Сырое'),
|
||
('fermented', 'ru', 'Ферментированное');
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- Seed data: dish_categories + dish_category_translations
|
||
-- ---------------------------------------------------------------------------
|
||
INSERT INTO dish_categories (slug, name, sort_order) VALUES
|
||
('soup', 'Soup', 1),
|
||
('salad', 'Salad', 2),
|
||
('main_course', 'Main Course', 3),
|
||
('side_dish', 'Side Dish', 4),
|
||
('appetizer', 'Appetizer', 5),
|
||
('dessert', 'Dessert', 6),
|
||
('breakfast', 'Breakfast', 7),
|
||
('drink', 'Drink', 8),
|
||
('bread', 'Bread', 9),
|
||
('sauce', 'Sauce', 10),
|
||
('snack', 'Snack', 11);
|
||
|
||
INSERT INTO dish_category_translations (category_slug, lang, name) VALUES
|
||
('soup', 'ru', 'Суп'),
|
||
('salad', 'ru', 'Салат'),
|
||
('main_course', 'ru', 'Основное блюдо'),
|
||
('side_dish', 'ru', 'Гарнир'),
|
||
('appetizer', 'ru', 'Закуска'),
|
||
('dessert', 'ru', 'Десерт'),
|
||
('breakfast', 'ru', 'Завтрак'),
|
||
('drink', 'ru', 'Напиток'),
|
||
('bread', 'ru', 'Выпечка'),
|
||
('sauce', 'ru', 'Соус'),
|
||
('snack', 'ru', 'Снэк');
|
||
|
||
-- +goose Down
|
||
DROP TABLE IF EXISTS meal_diary;
|
||
DROP TABLE IF EXISTS dish_recognition_jobs;
|
||
DROP TABLE IF EXISTS shopping_lists;
|
||
DROP TABLE IF EXISTS menu_items;
|
||
DROP TABLE IF EXISTS menu_plans;
|
||
DROP TABLE IF EXISTS user_saved_recipes;
|
||
DROP TABLE IF EXISTS user_product_components;
|
||
DROP TABLE IF EXISTS user_products;
|
||
DROP TABLE IF EXISTS recipe_step_translations;
|
||
DROP TABLE IF EXISTS recipe_steps;
|
||
DROP TABLE IF EXISTS recipe_ingredient_translations;
|
||
DROP TABLE IF EXISTS recipe_ingredients;
|
||
DROP TABLE IF EXISTS recipe_translations;
|
||
DROP TABLE IF EXISTS recipes;
|
||
DROP TABLE IF EXISTS dish_tags;
|
||
DROP TABLE IF EXISTS dish_translations;
|
||
DROP TABLE IF EXISTS dishes;
|
||
DROP TABLE IF EXISTS dish_category_translations;
|
||
DROP TABLE IF EXISTS dish_categories;
|
||
DROP TABLE IF EXISTS tag_translations;
|
||
DROP TABLE IF EXISTS tags;
|
||
DROP TABLE IF EXISTS cuisine_translations;
|
||
DROP TABLE IF EXISTS cuisines;
|
||
DROP TABLE IF EXISTS product_aliases;
|
||
DROP TABLE IF EXISTS products;
|
||
DROP TABLE IF EXISTS product_category_translations;
|
||
DROP TABLE IF EXISTS product_categories;
|
||
DROP TABLE IF EXISTS unit_translations;
|
||
DROP TABLE IF EXISTS units;
|
||
DROP TABLE IF EXISTS languages;
|
||
DROP TABLE IF EXISTS users;
|
||
DROP TYPE IF EXISTS recipe_difficulty;
|
||
DROP TYPE IF EXISTS recipe_source;
|
||
DROP TYPE IF EXISTS activity_level;
|
||
DROP TYPE IF EXISTS user_goal;
|
||
DROP TYPE IF EXISTS user_gender;
|
||
DROP TYPE IF EXISTS user_plan;
|
||
DROP FUNCTION IF EXISTS uuid_generate_v7();
|
||
DROP EXTENSION IF EXISTS pg_trgm;
|
||
DROP EXTENSION IF EXISTS pgcrypto;
|