feat: core schema redesign — dishes, structured recipes, cuisines, tags (iteration 7)
Replaces the flat JSONB-based recipe schema with a normalized relational model:
Schema (migrations consolidated to 001_initial_schema + 002_seed_data):
- New: dishes, dish_translations, dish_tags — canonical dish catalog
- New: cuisines, tags, dish_categories with _translations tables + full seed data
- New: recipe_ingredients, recipe_steps with _translations (replaces JSONB blobs)
- New: user_saved_recipes thin bookmark (drops saved_recipes + saved_recipe_translations)
- New: product_ingredients M2M table
- recipes: now a cooking variant of a dish (dish_id FK, no title/JSONB columns)
- recipe_translations: repurposed to per-language notes only
- products: mapping_id → primary_ingredient_id
- menu_items: recipe_id FK → recipes; adds dish_id
- meal_diary: adds dish_id, recipe_id → recipes, portion_g
Backend (Go):
- New packages: internal/cuisine, internal/tag, internal/dish (registry + handler + repo)
- New GET /cuisines, GET /tags (public), GET /dishes, GET /dishes/{id}, GET /recipes/{id}
- recipe, savedrecipe, menu, diary, product, ingredient packages updated for new schema
Flutter:
- New models: Cuisine, Tag; new providers: cuisineNamesProvider, tagNamesProvider
- recipe.dart: RecipeIngredient gains unit_code + effectiveUnit getter
- saved_recipe.dart: thin model, manual fromJson, computed nutrition getter
- diary_entry.dart: adds dishId, recipeId, portionG
- recipe_detail_screen.dart: localized cuisine/tag names via providers
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
452
backend/migrations/001_initial_schema.sql
Normal file
452
backend/migrations/001_initial_schema.sql
Normal file
@@ -0,0 +1,452 @@
|
||||
-- +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
|
||||
-- ---------------------------------------------------------------------------
|
||||
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;
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 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');
|
||||
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)
|
||||
);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- ingredient_categories + translations
|
||||
-- ---------------------------------------------------------------------------
|
||||
CREATE TABLE ingredient_categories (
|
||||
slug VARCHAR(50) PRIMARY KEY,
|
||||
sort_order SMALLINT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE TABLE ingredient_category_translations (
|
||||
category_slug VARCHAR(50) NOT NULL REFERENCES ingredient_categories(slug) ON DELETE CASCADE,
|
||||
lang VARCHAR(10) NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
PRIMARY KEY (category_slug, lang)
|
||||
);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- ingredients (canonical catalog — formerly ingredient_mappings)
|
||||
-- ---------------------------------------------------------------------------
|
||||
CREATE TABLE ingredients (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
|
||||
canonical_name VARCHAR(255) NOT NULL,
|
||||
category VARCHAR(50) REFERENCES ingredient_categories(slug),
|
||||
default_unit VARCHAR(20) REFERENCES units(code),
|
||||
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_ingredient_canonical_name UNIQUE (canonical_name)
|
||||
);
|
||||
CREATE INDEX idx_ingredients_canonical_name ON ingredients (canonical_name);
|
||||
CREATE INDEX idx_ingredients_category ON ingredients (category);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- ingredient_translations
|
||||
-- ---------------------------------------------------------------------------
|
||||
CREATE TABLE ingredient_translations (
|
||||
ingredient_id UUID NOT NULL REFERENCES ingredients(id) ON DELETE CASCADE,
|
||||
lang VARCHAR(10) NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
PRIMARY KEY (ingredient_id, lang)
|
||||
);
|
||||
CREATE INDEX idx_ingredient_translations_ingredient_id ON ingredient_translations (ingredient_id);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- ingredient_aliases (relational, replaces JSONB aliases column)
|
||||
-- ---------------------------------------------------------------------------
|
||||
CREATE TABLE ingredient_aliases (
|
||||
ingredient_id UUID NOT NULL REFERENCES ingredients(id) ON DELETE CASCADE,
|
||||
lang VARCHAR(10) NOT NULL,
|
||||
alias TEXT NOT NULL,
|
||||
PRIMARY KEY (ingredient_id, lang, alias)
|
||||
);
|
||||
CREATE INDEX idx_ingredient_aliases_lookup ON ingredient_aliases (ingredient_id, lang);
|
||||
CREATE INDEX idx_ingredient_aliases_trgm ON ingredient_aliases USING GIN (alias gin_trgm_ops);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 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 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 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 (per-language cooking notes only)
|
||||
-- ---------------------------------------------------------------------------
|
||||
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 ingredients(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)
|
||||
);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- products (user fridge / pantry items)
|
||||
-- ---------------------------------------------------------------------------
|
||||
CREATE TABLE products (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
primary_ingredient_id UUID REFERENCES ingredients(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_products_user_id ON products (user_id);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- product_ingredients (M2M: composite product ↔ ingredients)
|
||||
-- ---------------------------------------------------------------------------
|
||||
CREATE TABLE product_ingredients (
|
||||
product_id UUID NOT NULL REFERENCES products(id) ON DELETE CASCADE,
|
||||
ingredient_id UUID NOT NULL REFERENCES ingredients(id) ON DELETE CASCADE,
|
||||
amount_per_100g DECIMAL(10,2),
|
||||
PRIMARY KEY (product_id, ingredient_id)
|
||||
);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- user_saved_recipes (thin bookmark — content lives in dishes + 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','lunch','dinner')),
|
||||
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)
|
||||
);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 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,
|
||||
name TEXT NOT NULL,
|
||||
portions DECIMAL(5,2) NOT NULL DEFAULT 1,
|
||||
calories DECIMAL(8,2),
|
||||
protein_g DECIMAL(8,2),
|
||||
fat_g DECIMAL(8,2),
|
||||
carbs_g DECIMAL(8,2),
|
||||
source TEXT NOT NULL DEFAULT 'manual',
|
||||
dish_id UUID REFERENCES dishes(id) ON DELETE SET NULL,
|
||||
recipe_id UUID REFERENCES recipes(id) ON DELETE SET NULL,
|
||||
portion_g DECIMAL(10,2),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX idx_meal_diary_user_date ON meal_diary (user_id, date);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE IF EXISTS meal_diary;
|
||||
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 product_ingredients;
|
||||
DROP TABLE IF EXISTS 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 ingredient_aliases;
|
||||
DROP TABLE IF EXISTS ingredient_translations;
|
||||
DROP TABLE IF EXISTS ingredients;
|
||||
DROP TABLE IF EXISTS ingredient_category_translations;
|
||||
DROP TABLE IF EXISTS ingredient_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;
|
||||
Reference in New Issue
Block a user