postgres-init: 누적 ALTER 를 없애고 init.sql 한 벌로 간다
alters/*.sql 은 **기존 DB 를 보정**하는 파일이다. 그런데 이 프로젝트는 아직 git 에도 서버에도 올라가지 않아 보정할 기존 DB 가 없다. 파일마다 주석이 "신규 DB 는 init-data/init.sql 에 반영돼 있어 이 파일이 필요 없다"고 이미 적고 있었다. 지우기 전에 init.sql 이 여섯 개를 전부 담고 있는지 확인했다 — local_contents 발행 컬럼 4개, site template_id·theme, external_place_id/external_source, 그리고 allow-duplicate-places 가 지우는 두 인덱스가 애초에 없다는 것까지. 운영 DB 가 생기는 순간 이 디렉토리는 다시 필요해진다. 그때 되살린다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019uYhHQdssRubirPirrdJJC
This commit is contained in:
parent
7a0d933a7a
commit
139d460839
@ -1,41 +0,0 @@
|
||||
-- 사업장의 외부 장소 식별자를 카카오 전용에서 **소스 중립**으로 바꾼다.
|
||||
-- 신규 DB 는 init-data/init.sql 에 반영돼 있어 이 파일이 필요 없다(멱등이라 실행해도 무해).
|
||||
-- 적용: psql -h <host> -p <port> -U <user> -f postgres-init/alters/2026-08-27-external-place-id.sql
|
||||
--
|
||||
-- 왜 바꾸나
|
||||
-- 카카오 REST 키가 없어 네이버 지역검색으로 동일 업소를 검증하는데, **네이버에는 카카오의
|
||||
-- kakao_place_id 같은 안정적 고유 키가 없다**(응답의 link 는 업체 홈페이지지 플레이스 URL 이 아니다).
|
||||
-- 그대로 두면 네이버로 검증한 사업장은 external id 가 NULL 이라 중복 등록 방지 유니크가 걸리지 않고,
|
||||
-- 같은 가게를 두 번 등록해도 막히지 않는다.
|
||||
--
|
||||
-- → 식별자 컬럼을 소스 중립으로 바꾸고(external_source + external_place_id),
|
||||
-- 고유 키가 없는 소스를 위해 **(회사, 상호명, 도로명주소)** 대체 유니크를 둔다.
|
||||
-- 도로명주소만으로는 안 된다 — 한 건물에 여러 가게가 있다.
|
||||
|
||||
\connect web4ai_db
|
||||
|
||||
ALTER TABLE place.places
|
||||
RENAME COLUMN kakao_place_id TO external_place_id;
|
||||
|
||||
ALTER TABLE place.places
|
||||
ADD COLUMN IF NOT EXISTS external_source SMALLINT NULL; -- ExternalPlaceSource: 1=kakao 2=naver
|
||||
|
||||
-- 기존 값은 전부 카카오에서 온 것이다.
|
||||
UPDATE place.places SET external_source = 1
|
||||
WHERE external_place_id IS NOT NULL AND external_source IS NULL;
|
||||
|
||||
DROP INDEX IF EXISTS place.uq_places_company_kakao;
|
||||
|
||||
-- 고유 키가 있는 소스(카카오): 소스 + 외부 id 로 중복을 막는다.
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_places_company_external
|
||||
ON place.places (company_id, external_source, external_place_id)
|
||||
WHERE deleted = FALSE AND external_place_id IS NOT NULL;
|
||||
|
||||
-- 고유 키가 없는 소스(네이버): 상호명 + 도로명주소로 막는다.
|
||||
-- ★ 도로명주소만 쓰면 안 된다 — 한 건물에 카페와 식당이 같이 있다.
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_places_company_name_address
|
||||
ON place.places (company_id, name, road_address)
|
||||
WHERE deleted = FALSE AND road_address IS NOT NULL AND external_place_id IS NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_places_external ON place.places (external_source, external_place_id)
|
||||
WHERE deleted = FALSE AND external_place_id IS NOT NULL;
|
||||
@ -1,33 +0,0 @@
|
||||
-- fact 를 '노출값 1건 + 후보 N건' 모델로 바꾼다 — 재수집(업데이트) 프로세스를 담기 위함.
|
||||
-- 신규 DB 는 init-data/init.sql 에 이미 반영돼 있어 이 파일이 필요 없다(멱등이라 실행해도 무해).
|
||||
-- 적용: psql -h <host> -p <port> -U <user> -f postgres-init/alters/2026-08-27-fact-candidate-model.sql
|
||||
--
|
||||
-- 왜 바꾸나
|
||||
-- 기존: 활성 유니크가 status IN (1,2,3,4) 라 (사업장,단위,key) 당 살아있는 fact 가 1건뿐이었다.
|
||||
-- → 재수집이 오면 확인된 노출값을 반드시 밀어내야 했고, 그 순간 사이트에서 사실이 사라졌다.
|
||||
-- → 값이 그대로여도 검증(VERIFIED)이 초기화됐다.
|
||||
-- 변경: 유니크를 **노출 상태(3=VERIFIED, 4=CORRECTED)에만** 건다.
|
||||
-- → 사이트에 나가는 값은 여전히 1건(두 값으로 갈라지지 않는다)
|
||||
-- → 후보(1=UNVERIFIED, 2=PENDING_OWNER)는 여러 건 공존 가능
|
||||
-- → 재수집은 노출값을 건드리지 않고 후보로 쌓이고, 사람이 승인할 때 교체된다
|
||||
|
||||
\connect web4ai_db
|
||||
|
||||
DROP INDEX IF EXISTS fact.uq_facts_place_key;
|
||||
DROP INDEX IF EXISTS fact.uq_facts_unit_key;
|
||||
|
||||
-- 노출값은 (사업장, 단위, key) 당 1건. 후보·이력은 제외한다.
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_facts_published_place_key ON fact.facts (place_id, key)
|
||||
WHERE deleted = FALSE AND unit_id IS NULL AND status IN (3, 4);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_facts_published_unit_key ON fact.facts (place_id, unit_id, key)
|
||||
WHERE deleted = FALSE AND unit_id IS NOT NULL AND status IN (3, 4);
|
||||
|
||||
-- 후보 조회 경로(사람 확인 큐) — 재수집이 올려놓은 대기 항목을 훑는다.
|
||||
CREATE INDEX IF NOT EXISTS idx_facts_candidate ON fact.facts (place_id, key, source_type)
|
||||
WHERE deleted = FALSE AND status IN (1, 2);
|
||||
|
||||
-- 노출값이 실제로 바뀐 시각. ★ 개별 재빌드 대상 판별용 —
|
||||
-- site_versions.built_at < places.content_updated_at 인 사이트만 다시 빌드하면 된다.
|
||||
-- (사이트 1,000개에서 전체 재빌드는 못 쓴다.)
|
||||
ALTER TABLE place.places
|
||||
ADD COLUMN IF NOT EXISTS content_updated_at TIMESTAMPTZ NULL;
|
||||
@ -1,10 +0,0 @@
|
||||
ALTER TABLE local.local_contents
|
||||
ADD COLUMN IF NOT EXISTS status SMALLINT NOT NULL DEFAULT 1,
|
||||
ADD COLUMN IF NOT EXISTS published_at TIMESTAMPTZ NULL,
|
||||
ADD COLUMN IF NOT EXISTS published_by uuid NULL,
|
||||
ADD COLUMN IF NOT EXISTS display_start_at TIMESTAMPTZ NULL,
|
||||
ADD COLUMN IF NOT EXISTS display_end_at TIMESTAMPTZ NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_local_contents_status
|
||||
ON local.local_contents (status, collected_at DESC)
|
||||
WHERE deleted = false;
|
||||
@ -1,12 +0,0 @@
|
||||
-- 사이트 템플릿 저장.
|
||||
--
|
||||
-- 위저드 4단계에서 사장님이 고른 템플릿이 브라우저 상태로만 남아 있었다.
|
||||
-- 그래서 발행 잡(services/site_payload)이 그 값을 읽을 곳이 없어 업종 기본 템플릿으로
|
||||
-- 굽고 있었고, 사장님이 고른 디자인과 실제 발행본이 달랐다.
|
||||
--
|
||||
-- template_id 는 프론트의 배리에이션 레지스트리 키다(예: 'stay-o2o-editorial').
|
||||
-- 서버는 값을 해석하지 않고 그대로 보관·반환만 한다 — 템플릿 목록은 프론트가 소유한다.
|
||||
ALTER TABLE site.sites ADD COLUMN IF NOT EXISTS template_id varchar(100);
|
||||
|
||||
COMMENT ON COLUMN site.sites.template_id IS
|
||||
'사장님이 고른 템플릿 키. NULL 이면 업종 기본 템플릿으로 굽는다.';
|
||||
@ -1,3 +0,0 @@
|
||||
-- 한 사용자가 같은 실제 업장을 여러 사업장 프로젝트로 등록할 수 있게 한다.
|
||||
DROP INDEX IF EXISTS place.uq_places_company_external;
|
||||
DROP INDEX IF EXISTS place.uq_places_company_name_address;
|
||||
@ -1,23 +0,0 @@
|
||||
-- 사이트 테마(색·서체·섹션) 저장.
|
||||
--
|
||||
-- 관리자 에디터에서 사장님이 바꿀 수 있는 디자인은 6가지다:
|
||||
-- 템플릿 · 섹션 on/off · 섹션 순서 · 섹션별 배리에이션 · 색 · 서체
|
||||
-- 그중 저장되는 자리가 있던 건 template_id 하나뿐이었다. 나머지 다섯은 브라우저 메모리에만 있다가
|
||||
-- 새로고침하면 사라졌고, 발행 잡(services/site_payload)은 읽을 곳이 없어 업종 기본 표를 그대로 구웠다.
|
||||
-- 그래서 사장님이 섹션을 끄고 순서를 바꿔도 발행본은 언제나 업종 기본 모양으로 나갔다 —
|
||||
-- template_id 컬럼이 생긴 것과 정확히 같은 이유이고, 남은 다섯을 담을 자리가 이 컬럼이다.
|
||||
--
|
||||
-- ★ 왜 컬럼 5개가 아니라 jsonb 1개인가
|
||||
-- 섹션 목록·배리에이션 키·색 토큰 이름은 전부 프론트(배리에이션 레지스트리)가 소유한다.
|
||||
-- 관계형 컬럼으로 펼치면 프론트가 섹션이나 색 토큰을 하나 늘릴 때마다 마이그레이션이 따라와야 한다.
|
||||
-- 서버는 이 값을 해석하지 않고 보관·반환만 하므로 통째로 담는 것이 맞다.
|
||||
--
|
||||
-- ★ templateId 는 여기 넣지 않는다.
|
||||
-- 이미 sites.template_id 컬럼이 있다. 두 곳에 두면 어느 쪽이 진짜인지 갈리고,
|
||||
-- uq/조회가 걸린 쪽(컬럼)과 payload 가 읽는 쪽(jsonb)이 어긋나는 순간 화면과 발행본이 달라진다.
|
||||
ALTER TABLE site.sites ADD COLUMN IF NOT EXISTS theme jsonb NULL;
|
||||
|
||||
COMMENT ON COLUMN site.sites.theme IS
|
||||
'에디터가 정한 디자인. {"colors":{...},"fontStyle":"...","sections":[{"id","name","enabled","locked","variantId"}]} '
|
||||
'— 서버는 해석하지 않고 그대로 보관·반환한다(목록은 프론트가 소유). '
|
||||
'NULL 이면 발행 잡이 업종 기본 색·서체·섹션으로 굽는다. templateId 는 sites.template_id 가 소유한다.';
|
||||
Loading…
Reference in New Issue
Block a user