-- 0008 · 공용과 개인화를 이름으로 가른다 -- -- area_* 공용. 지역 단위, 여러 사이트가 나눠 쓴다 → 렌더러 모양 그대로 담는다. -- site_sections 개인화 전부(거리·숨김·순서·사장님 편집). -- -- 지금은 body 가 TourAPI 원문 이름이고(빌드마다 바꿔 왔다), 거리·숨김이 공용 쪽에 있다. -- 1. 타입명을 모든 행에 채운다 -- 인덱스를 먼저 뗀다 — 옛 (region_code, kind) 는 같은 지역 관광지 여러 건이 전부 -- kind='attraction' 이 되는 순간 부딪친다. 장소류 유일성은 (source, external_id) 가 잡는다. DROP INDEX IF EXISTS public.uq_local_contents_kind; UPDATE public.area_contents SET kind = CASE content_type WHEN 1 THEN 'weather' WHEN 2 THEN 'festival' WHEN 3 THEN 'attraction' WHEN 4 THEN 'restaurant' WHEN 5 THEN 'course' END, updated_at = now() WHERE deleted = FALSE AND kind IS NULL AND content_type BETWEEN 1 AND 5; CREATE UNIQUE INDEX IF NOT EXISTS uq_local_contents_kind ON public.area_contents (region_code, kind) WHERE deleted = FALSE AND kind IS NOT NULL AND external_id IS NULL; -- 2. body 를 렌더러 모양으로 -- 좌표는 뺀다 — latitude/longitude 컬럼이 그 자리다(0004). -- lclsSystm2 는 남긴다 — 주변 맛집에서 같은 업태를 뺄 때 쓴다. UPDATE public.area_contents SET body = ( jsonb_strip_nulls(jsonb_build_object( 'name', COALESCE(NULLIF(body->>'title', ''), title), 'searchQuery', COALESCE(NULLIF(body->>'title', ''), title), 'location', NULLIF(body->>'addr1', ''), 'imageUrl', NULLIF(body->>'firstimage', ''), 'lclsSystm2', NULLIF(body->>'lclsSystm2', '') )) -- 축제는 날짜 원값을 남긴다 — 노출 기간 필터가 읽는다. || CASE WHEN content_type = 2 THEN jsonb_strip_nulls(jsonb_build_object( 'eventstartdate', NULLIF(body->>'eventstartdate', ''), 'eventenddate', NULLIF(body->>'eventenddate', ''), 'homepage', NULLIF(body->>'homepage', ''), 'overview', NULLIF(body->>'overview', '') )) ELSE '{}'::jsonb END ), updated_at = now() WHERE deleted = FALSE AND content_type BETWEEN 2 AND 5 AND body ? 'contentid'; -- 3. 거리·숨김을 사이트 쪽으로 -- 사이트가 없는 업장은 다음 수집이 다시 쓴다 — 여기서 sites 행을 만들지 않는다. -- items 배열이 아니라 ref → 값 맵이다. 순서는 읽는 쪽이 정한다. INSERT INTO public.site_sections (site_id, section_id, data, source_type, status, sort_order) SELECT s.site_id, 'local', jsonb_build_object('kind', 'local', 'places', COALESCE(jsonb_object_agg( r.local_content_id::text, jsonb_build_object('kind', a.kind, 'distanceMeters', r.distance_m, 'hidden', r.hidden) ), '{}'::jsonb)), 2, -- SourceType.API — 사람이 쓴 게 아니라 수집이 만든 개인화 값이다 1, 0 FROM public.place_area_refs r JOIN public.area_contents a ON a.local_content_id = r.local_content_id AND a.deleted = FALSE JOIN public.sites s ON s.place_id = r.place_id AND s.deleted = FALSE WHERE r.deleted = FALSE GROUP BY s.site_id ON CONFLICT DO NOTHING; -- place_area_refs 는 아직 지우지 않는다. 읽는 코드가 옮겨 간 것을 확인한 뒤 별도 번호로 뗀다 — -- 같은 마이그레이션에서 옮기고 지우면, 이관이 틀렸을 때 되돌릴 원본이 없다.