o2o-negosium-original/postgres-init/dbeaver/7_lps_source_state_dbeaver.sql
민헌 74db218410 feat(lps): 2단계 — price_history 에 몰별 확인 상태 저장
1단계에서 만든 SourceState 가 job.result 에만 있어 화면까지 못 갔다. by_mall 은 **가격이 있는
몰만** 담으므로, 빠진 몰이 '거기엔 없더라'인지 '거기를 못 봤다'인지 구분할 자리가 없었다.

- price_history.sources (JSONB): 몰별 상태를 그대로 담는다.
  {"naver": {"state": "matched", "count": 40}, "coupang": {"state": "blocked", "error": "..."}}
  열린 스키마라 몰이 늘거나 근거를 덧붙여도 마이그레이션이 필요 없다(by_mall 과 같은 방침).
- price_history.partial (bool): 결과가 완전한가. sources 에서 유도 가능하지만 컬럼으로 둔다 —
  소비자가 '어떤 상태가 확인된 것인가'라는 판단 규칙까지 알아야 하면 **상태 정의가 두 곳으로
  흩어진다**. 판단은 LPS 가 끝내고 소비자(negodata·lps-admin)는 사실 하나만 읽는다.
- 부분 인덱스 ix_price_history_partial — partial=true 행만 담아 작게 유지(운영 점검·알림용).
- _record_history 가 per_source 를 받아 partial 을 계산해 기록한다. 네거티브 캐시 히트 경로는
  sources 없이 남긴다(부분 결과는 애초에 캐시하지 않으므로 항상 확정).
- 마이그레이션: postgres-init/dbeaver/7_lps_source_state_dbeaver.sql (재실행 안전, **운영 적용 필요**)

검증(로컬 실 DB): 쿠팡 차단 vs 쿠팡 0건은 by_mall 이 둘 다 ['naver'] 로 같지만
partial(true/false)·sources.coupang.state(blocked/empty)가 두 경우를 갈라낸다.
JSONB 는 ensure_ascii=False 로 한글 사유가 깨지지 않는 것도 테스트로 고정.

테스트 3건 추가, 전체 289 passed. 진행 상황은 docs/result-states.md 4절.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 10:57:19 +09:00

33 lines
2.2 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- LPS 몰별 확인 상태 — **lps_db 에 연결해서 실행**. 재실행 안전(IF NOT EXISTS).
--
-- 왜 필요한가: '그 몰에 더 싼 게 없었다'와 '그 몰이 막혀서 못 봤다'가 지금 화면에서 똑같이
-- '' 로 보인다. 사용자는 앞쪽으로 읽지만 실제로는 뒤쪽일 수 있다 — 안 본 걸 없다고 말하는 셈이다.
-- 크롤러는 그 차이를 이미 알고 있는데(SourceState), 담을 자리가 없어 화면까지 못 갔다.
--
-- 상태 정의와 표기 규칙은 lps/docs/result-states.md 가 소스다.
-- 몰별 확인 상태 — {"naver": {"state": "matched", "count": 40},
-- "coupang": {"state": "blocked", "error": "AdapterError: ..."}}
-- 열린 스키마(JSONB)로 둔다: 몰이 늘거나 상태에 근거를 덧붙여도 마이그레이션이 필요 없다.
-- state 값: matched / no_match / empty / blocked / env_blocked / unavailable / skipped
ALTER TABLE price_history ADD COLUMN IF NOT EXISTS sources JSONB;
-- 결과가 **완전한가**. true = 못 본 몰이 있어 이 값이 최종이 아니다.
-- sources 에서 유도할 수 있지만 굳이 컬럼으로 둔다 — 소비자(negodata)가 '어떤 상태가 확인된
-- 것인가'라는 판단 규칙까지 알아야 하면 상태 정의가 두 곳으로 흩어진다. 판단은 LPS 가 하고,
-- 소비자는 사실 하나만 읽게 한다.
ALTER TABLE price_history ADD COLUMN IF NOT EXISTS partial BOOLEAN NOT NULL DEFAULT FALSE;
-- '확인 못한 결과'만 빠르게 뽑기 위한 부분 인덱스(운영 점검·알림용).
-- 전체가 아니라 partial=true 행만 담아 인덱스가 작게 유지된다.
CREATE INDEX IF NOT EXISTS ix_price_history_partial
ON price_history (triggered_at) WHERE partial;
-- ── 검증 ──────────────────────────────────────────────────────────────────────
SELECT column_name, data_type,
CASE WHEN column_name IN ('sources','partial') THEN '이번 추가' ELSE '' END AS note
FROM information_schema.columns
WHERE table_name = 'price_history'
AND column_name IN ('outcome','by_mall','sources','partial')
ORDER BY column_name;