gohorsejobs/backend/migrations/021_location_hierarchy.sql
Tiago Yamamoto 1018da8036 feat(location): add comprehensive geographic hierarchy (continents, countries, states, cities)
- Add migration 021_location_hierarchy.sql with new table structure
- Add location-loader.js seeder to import SQL dumps
- Update all seeder files to use country_id instead of region_id
- Rename companies.region_id to country_id
2025-12-24 15:04:21 -03:00

158 lines
6 KiB
SQL

-- Migration: 021_location_hierarchy.sql
-- Description: Restructure location tables to use comprehensive geographic hierarchy
-- Data Source: GeoDB Cities (https://github.com/dr5hn/countries-states-cities-database)
-- ============================================================================
-- PHASE 1: Backup existing tables
-- ============================================================================
-- Rename old tables to preserve data
ALTER TABLE IF EXISTS regions RENAME TO regions_old;
ALTER TABLE IF EXISTS cities RENAME TO cities_old;
-- Drop old indexes (they will conflict)
DROP INDEX IF EXISTS idx_regions_country;
DROP INDEX IF EXISTS idx_cities_region;
-- ============================================================================
-- PHASE 2: Create new location hierarchy
-- ============================================================================
-- 2.1 Continents (formerly "regions" in GeoDB)
CREATE TABLE IF NOT EXISTS continents (
id BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
name VARCHAR(100) NOT NULL,
translations TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
flag SMALLINT DEFAULT 1 NOT NULL,
wiki_data_id VARCHAR(255)
);
COMMENT ON TABLE continents IS 'Geographic continents (Africa, Americas, Asia, Europe, Oceania, Polar)';
-- 2.2 Subregions (e.g., Northern Africa, South America)
CREATE TABLE IF NOT EXISTS subregions (
id BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
name VARCHAR(100) NOT NULL,
continent_id BIGINT NOT NULL REFERENCES continents(id),
translations TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
flag SMALLINT DEFAULT 1 NOT NULL,
wiki_data_id VARCHAR(255)
);
CREATE INDEX idx_subregions_continent ON subregions(continent_id);
COMMENT ON TABLE subregions IS 'Geographic subregions within continents';
-- 2.3 Countries
CREATE TABLE IF NOT EXISTS countries (
id BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
name VARCHAR(100) NOT NULL,
iso2 CHAR(2),
iso3 CHAR(3),
numeric_code CHAR(3),
phonecode VARCHAR(255),
capital VARCHAR(255),
currency VARCHAR(255),
currency_name VARCHAR(255),
currency_symbol VARCHAR(255),
tld VARCHAR(255),
native VARCHAR(255),
continent_id BIGINT REFERENCES continents(id),
subregion_id BIGINT REFERENCES subregions(id),
nationality VARCHAR(255),
latitude DECIMAL(10,8),
longitude DECIMAL(11,8),
emoji VARCHAR(10),
emoji_u VARCHAR(50),
timezones TEXT,
translations TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
flag SMALLINT DEFAULT 1 NOT NULL,
wiki_data_id VARCHAR(255)
);
CREATE INDEX idx_countries_iso2 ON countries(iso2);
CREATE INDEX idx_countries_iso3 ON countries(iso3);
CREATE INDEX idx_countries_continent ON countries(continent_id);
CREATE INDEX idx_countries_subregion ON countries(subregion_id);
COMMENT ON TABLE countries IS 'All countries with ISO codes, currencies, and metadata';
-- 2.4 States/Provinces
CREATE TABLE IF NOT EXISTS states (
id BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
name VARCHAR(255) NOT NULL,
country_id BIGINT NOT NULL REFERENCES countries(id),
country_code CHAR(2) NOT NULL,
iso2 VARCHAR(10),
fips_code VARCHAR(255),
type VARCHAR(191),
latitude DECIMAL(10,8),
longitude DECIMAL(11,8),
timezone VARCHAR(255),
translations TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
flag SMALLINT DEFAULT 1 NOT NULL,
wiki_data_id VARCHAR(255)
);
CREATE INDEX idx_states_country ON states(country_id);
CREATE INDEX idx_states_country_code ON states(country_code);
CREATE INDEX idx_states_iso2 ON states(iso2);
COMMENT ON TABLE states IS 'States, provinces, and administrative regions';
-- 2.5 Cities
CREATE TABLE IF NOT EXISTS cities (
id BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
name VARCHAR(255) NOT NULL,
state_id BIGINT NOT NULL REFERENCES states(id),
state_code VARCHAR(255),
country_id BIGINT NOT NULL REFERENCES countries(id),
country_code CHAR(2) NOT NULL,
latitude DECIMAL(10,8) NOT NULL,
longitude DECIMAL(11,8) NOT NULL,
population BIGINT,
timezone VARCHAR(255),
translations TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
flag SMALLINT DEFAULT 1 NOT NULL,
wiki_data_id VARCHAR(255)
);
CREATE INDEX idx_cities_state ON cities(state_id);
CREATE INDEX idx_cities_country ON cities(country_id);
CREATE INDEX idx_cities_country_code ON cities(country_code);
CREATE INDEX idx_cities_population ON cities(population);
COMMENT ON TABLE cities IS 'Cities with coordinates and population data';
-- ============================================================================
-- PHASE 3: Update companies table FKs
-- ============================================================================
-- Drop old FK constraint if exists (ignore errors)
ALTER TABLE companies DROP CONSTRAINT IF EXISTS companies_region_id_fkey;
ALTER TABLE companies DROP CONSTRAINT IF EXISTS companies_city_id_fkey;
-- Update column types to BIGINT (to match new tables)
ALTER TABLE companies ALTER COLUMN region_id TYPE BIGINT USING region_id::BIGINT;
ALTER TABLE companies ALTER COLUMN city_id TYPE BIGINT USING city_id::BIGINT;
-- Rename region_id to country_id for clarity
ALTER TABLE companies RENAME COLUMN region_id TO country_id;
-- Add new FK constraints (without REFERENCES for now - data will be populated by seeder)
-- These will be validated after seeder populates the data
-- Update indexes
DROP INDEX IF EXISTS idx_companies_region;
CREATE INDEX idx_companies_country ON companies(country_id);
CREATE INDEX idx_companies_city ON companies(city_id);
-- Add comments
COMMENT ON COLUMN companies.country_id IS 'Reference to countries table';
COMMENT ON COLUMN companies.city_id IS 'Reference to cities table';