쿼리 표현력은 적합하다(31개 중 19 지원, 불가 7개 중 4개는 v2가 의도적으로 죽인 것). 그러나 "최적"이라고는 말할 수 없다 — 모델링 건전성 점검에서 결함 3건이 나왔고(§⑤), 성능은 실데이터 없이 판단 불가다.
쿼리 관점 갭 3개는 전부 같은 원인이다 — "입력을 담을 자리는 있는데 그 소셜/필드를 수집 대상에서 뺐다". 모델링 관점 갭 3개는 다른 원인이다 — "어디에 두는지·누가 SoT인지를 안 정했다".
이 조사는 쿼리 31개(Category A~E · §5 시그널 · v2 Q1~Q10 · 반응관측)만 대조했고, structural-idea-matching.html 의 구조 축 88개는 보지 않았다. 합치면 119축이다.
88축을 별도로 훑은 결과(v3 §④·§⑥·§⑭ 반영):
links.lineage[] 도입으로
인용 경유 재사용(elon 패턴)이 잡힌다주의 — structural-idea-matching 은 규칙상 "데이터 유무만 표시, 유효성 평가 안 함" 이라 "데이터가 있어도 구조상 무의미" 는 걸러내지 못한다. 그리고 그 축들은 채택 목록이 아니라 검증 대기열이다(research-findings).
초판(§①~④)은 쿼리 커버리지만 봤다 — "조사된 쿼리가 이 스키마로 돌아가나". 엔티티 경계·문서 크기·SoT 중복·참조 갱신 규칙·컬렉션 분할 실익 같은 모델링 관점은 빠져 있었다. §⑤가 그 점검이며, 그 결과 §⑥의 대안 판정 2건도 수정됐다.
| 쿼리 | 판정 | 근거 · v3에서 달라진 점 |
|---|---|---|
| A-1 토큰 → 연관 콘텐츠 | 지원 | content_token_links{tokenAddress} → contents. v2가 배열을 엣지로 바꿔 1홉 늘었지만 as-of 절단이 가능해진 대가 |
| A-2 콘텐츠 → 파생 토큰 | 지원 | 같은 엣지 역방향. idx(contentKey, linkedAt) |
| A-3 계정 → 포트폴리오 | 지원 정확도↑ | v3가 creatorId를 숫자 id로 확정 → 핸들 리네임 시 누락되던 것이 사라짐. TikTok·GitHub은 재할당까지 있어 기존 handle 기준이면 다른 계정이 섞였을 케이스 |
| 쿼리 | 판정 | 근거 |
|---|---|---|
| B-1 승률≥0.7 S급 계정의 실시간 샤우팅 | 의도된 폐기 | v2가 quantMetrics.winRate·tier 제거 — "정의상 미래 성과"라 마스터에 두면 look-ahead |
| B-2 하이프 주도자가 진짜 알파콜러인가 | 의도된 폐기 | 동일 사유. 승률 없이 "이 계정이 이전에 몇 개 토큰에 붙었나"(Q3)로 대체 가능하나 같은 질문은 아님 |
| B-3 팔로워 급증 Z-Score 선행 탐지 | 의도된 폐기 | v2가 account_metrics_ts 제거 — "관측대상 = 조작대상"(팔로워는 매수 가능) |
v1 available-queries.md는 Category B를 "퀀트 관점에서 가장 가치가 높은" 경로로 명시했다.
그런데 v2가 그 입력(winRate·계정 시계열)을 제거하면서 3개 경로가 통째로 죽었고, v3는 이를 언급하지 않는다.
나중에 이 문서를 읽는 사람이 "왜 B가 안 되지"에서 막힌다. → v3 §⑩에 "폐기된 쿼리" 항목 추가 필요.
| 쿼리 | 판정 | 근거 |
|---|---|---|
| C-1 라벨 클러스터 토큰 바스켓 | 조건부 | entities[] 자리는 v2가 만들었으나 라벨 생성 방법이 미정(v2가 스스로 명시). v3도 해결 안 함 |
| C-2 플랫폼별 영향력 비교 | 지원 | platform 필드 + tokens 시총으로 그룹 비교. 비교 대상이 토큰 시총이라 소셜 지표 통일이 애초에 불필요 — v3의 metrics 통일 철회가 이 쿼리를 깨지 않음을 확인 |
| 쿼리 | 판정 | 근거 |
|---|---|---|
| D-1 바이럴 속도 상위 1% 발굴 | 지원 | v3가 ContentMetricSnapshot을 되살려 Δ 계산 가능. 단 관측 주기가 미정(§⑩) — "15분 Δ"를 재려면 그보다 촘촘해야 함 |
| D-2 시세 스파이크 원인 역추적 | 조건부 | 토큰 가격 시계열이 이 스키마 밖(기존 TokenPriceTracker 도메인). 크로스 도메인 조인이 전제인데 v3가 그 의존을 명시하지 않음 |
| D-3 소셜-시총 다이버전스 | 조건부 | D-2의 의존 + metricRanks 코호트 정의 미정. 소셜 지표는 raw로 두고 백분위로 비교하는 v3 결론과는 정합 |
| 쿼리 | 판정 | 근거 |
|---|---|---|
| E-1 인용 계보 · 밈 파생 트리 | 지원 대폭 강화 | v2는 "fetcher가 인용 원본 id를 안 담는 게 병목"이라 했으나, audit 실측에서 X quoted_tweet이 최상위와 키 집합 100% 동일하고 author까지 완전 프로필 → 추가 fetch 0회로 2단계 전개. v3에서 가장 많이 좋아진 경로 |
| E-2 지갑 ↔ 크로스플랫폼 엔티티 | 지원 | accounts.knownWallets[] 유지 + AF는 쿼리 시점 조인. 단 deployer 미수집은 v1이 지적한 그대로 남음 |
| E-3 코-멘션 네트워크 | 지원 신규 | v3가 mentions[]를 L1 공통축으로 승격 → IG·TikTok·X 3개 소셜에서 동작. 이전엔 X 하나뿐이었고 그마저 미저장 |
| 시그널 | 판정 | 근거 |
|---|---|---|
| 5.1-a Δt_meme (원본→첫 트윗 시차) | 조건부 | rootContentId가 필요한데 v2의 "순번 미저장" 원칙상 root도 질의 시점 파생이 자연스럽다. 파생 규칙이 미정 |
| 5.1-b Δt_token → isPreMinedHype | 지원 | tokens.createdAt − contents.core.publishedAt. 양쪽 다 immutable이라 look-ahead safe |
| 5.2 Sybil/Rug 위험도 | 지원 | 신생계정 = accounts.createdAt(v3가 채움률 표로 정리 — IG만 구조적 부재) + 동시인용 급증 = Q2 경쟁밀도 |
| 5.3 narrativeDepth | 지원 | genealogy.parentContentId 재귀. E-1과 같은 근거로 강화됨 |
| 5.4 Trust Divergence | 불가 | 두 겹으로 막힘 — ①v1이 이미 "산출식 정의 부재"로 판정 불가 ②도메인 신뢰도 입력(urlscanScore·certValidFrom)이 website 계열에 있는데 v3가 그 소셜을 다루지 않음(§②-1) |
| 5.5 I_total (승률가중 바이럴 강도) | 의도된 폐기 | winRate 의존 → Category B와 함께 죽음 |
| 쿼리 | 판정 | 근거 |
|---|---|---|
| Q1 as-of 순번(몇 번째 토큰인가) | 지원 | idx(contentKey, linkedAt) |
| Q2 동시 경쟁 밀도 | 지원 | 동일 인덱스 · 윈도우 상한이 T라 look-ahead 없음 |
| Q3 계정의 시리얼 발행 횟수 | 지원 정확도↑ | idx(creatorId, publishedAt). A-3과 같은 이유로 숫자 id 확정이 정확도를 올림 |
| Q4 같은 인프라로 뿌린 토큰 수(공장 규모) | 불가 | 🔴 fingerprint.trackingIds의 유일한 공급원이 website fetcher인데 v3에 website가 없음(§②-1) |
| Q5 인용 계보 깊이 | 지원 | E-1·5.3과 동일 |
| Q6 링크 살아있나/삭제됐나/없었나 | 지원 | fetchStatus 6종 + v3의 moderation 축(removedBy·archived·unavailable)이 "삭제 주체"까지 구분 |
| Q7 코호트 AF 자금 집중도 | 지원 | links가 코호트를 만들고 AF는 쿼리 시점 조인 |
| Q8 소셜 상위인데 AF 하위(괴리) | 지원 | 발행 시각 윈도우 코호트 + 두 백분위 차 |
| Q9 소재(엔티티) 포화도 | 조건부 | C-1과 동일 — entities[] 자리만 있고 라벨 생성 미정 |
| Q10 토큰 이미지 ↔ 프로필 이미지 동일성 | 불가 | 🔴 audit이 미디어 URL을 대부분 skip → contents.imageHash 계산 입력이 없음(§②-2) |
| 쿼리 | 판정 | 근거 |
|---|---|---|
| t_c ~ t_0 반응 궤적 토큰 발행 전 구간의 깨끗한 참여 궤적 | 구조 지원 | ContentMetricSnapshot이 content_key 기준이라 토큰과 무관하게 관측 가능하고,
content_token_links.linkedAt으로 t_0 이후 오염 구간을 잘라낼 수 있다.
다만 "fetch → 모니터링" 수집 방식 전환이 전제인데 그건 스키마 문제가 아니다 |
[정정] 초판은 이를 "audit이 빠뜨렸다"로 적었으나 틀렸다.
REFINEMENT-WORKFLOW.md의 객체 모델 표는 website를
"(별개) 프로젝트 자체 홈, 소셜 객체 아님"으로 의도적으로 3객체 밖에 분류해 뒀다.
즉 소셜 audit 대상이 아닌 게 맞다. 진짜 문제는 그 "별개 축"을 스키마 v3가 반영하지 않은 것이다.
코드의 SocialSourceType에는 'website'가 실재하고, website.fetcher.ts는 이미 17개 필드를 수집한다:
// urlscan certValidFrom · certIssuer · urlscanScore · brands · scanTime ip · asn · asnName · server · country · city technologies · bodyHashes · globalNames · trackingIds · urlscanLinkDomains // RDAP — 도메인 등록일 3필드
객체 모델이 "별개"로 분류했으므로 Creator/Content/Venue 어디에도 안 들어가는 게 맞다.
그런데 v3는 그 별개 축을 위한 자리를 만들지 않았다 — platform 코드(ig·yt·tt·x·rd·gh·tg)에도 없고 컬렉션도 없다.
샘플 디렉터리에 website.json이 없어 관측량조차 모른다.
파급: v2가 신설한 fingerprint.trackingIds(Q4 공장 규모)와 §5.4 Trust Divergence의
유일한 공급원이 이 fetcher다. v2는 contents.fingerprint로 자리를 만들었는데,
v3가 contents를 3객체 기준으로 재정의하면서 website가 그 안에 안 들어간다는 사실을 반영하지 않았다.
→ 조치는 소셜 audit이 아니라 §⑥(d) Infra 객체 신설이다. 객체 모델이 이미 "별개"라고 말했으므로 4번째 객체를 인정하는 게 그 분류와 정합한다. 다만 필드 목록 확정에는 website fetcher 산출 조사가 선행돼야 한다.
참고: information-inventory가 이미 "F only 17건 — 수집하는데 담을 자리가 없다"로 이 필드들을 지목했고, v2가 fingerprint 필드로 자리를 만들었다. 이제 반대 상황이다 — 자리는 있는데 소셜 정의가 없다.
v2 Q10은 tokens.imageHash ≈ contents.imageHash로 사전 공모 정황을 잡는 쿼리다(pump.fun 이미지 불변 + authority 소각 92% 실측이 전제).
그런데 audit은 미디어 URL을 대부분 skip했다 — perceptual hash 인프라가 없다는 일관된 사유였다:
| 소셜 | 미디어 필드 | audit 결정 |
|---|---|---|
displayUrl·videoUrl | skip | |
| x | extendedEntities.media | skip |
| github | avatar·originalAvatarUrl | skip |
| tiktok | slideshowImageLinks[] | keep — 유일 |
판단: 두 결정 모두 각자 맥락에선 옳다. 충돌은 "인프라가 없으니 안 담는다"와 "이 쿼리를 하려면 담아야 한다"가 서로를 모르는 상태에서 생겼다. → 해상도: URL만 담고 해시는 나중에(저장비용 작음) 또는 Q10을 명시적으로 보류. 지금처럼 조용히 불가인 게 최악이다.
platform 키가 한 문서에 두 번v3 §⑤ 구조 예시가 같은 문서에 platform을 두 가지 의미로 쓴다:
contents {
platform: "x", // ① 소셜 구분 문자열
…
platform: { x: { conversationId, … } }, // ② L2 네임스페이스 객체
}
같은 키를 덮어쓴다. 순수한 작성 실수이며 반드시 이름을 갈라야 한다 —
예: platform(구분) + ext 또는 platformData(네임스페이스).
v3는 지표에 대해서만 "소셜 차이를 아는 파일은 하나"를 정했다(METRIC_SPEC).
그런데 L1 공통축(text.primary·links·form·genealogy…) 매핑도 똑같이 소셜별 지식이다.
v3는 그걸 표로만 정의하고 어디에 구현할지 정하지 않았다.
→ 그대로 두면 fetcher마다 매핑 코드가 흩어지고, 지표는 레지스트리로 모으면서 나머지는 흩뜨리는 비일관 상태가 된다.
ContentGenerator(소셜별 · URL 1건당 1개)를 같은 위치에 두는 게 맞다.
이 조사에서는 처음에 CORE_MAP 이라 불렀으나, 실제로 1 URL → N 객체 생성이라 v3 §⑬ 에서 ContentGenerator 로 확정했다.
Category B 3개 + §5.5 = 4개 경로가 v2 결정으로 죽었다(winRate·account_metrics_ts 제거). 타당한 폐기지만 어디에도 기록돼 있지 않다 — v1 문서는 여전히 B를 "가장 가치 높은 카테고리"라고 말한다.
→ v3 §⑩에 "폐기된 쿼리와 사유" 표를 추가해야 한다. 안 그러면 나중에 같은 쿼리를 다시 설계하려다 입력이 없어 막힌다.
| 항목 | 평가 | 내용 |
|---|---|---|
| 레지스트리 패턴 | 적합 | 기존 SocialFetcherRouter.classify() + fetcher별 handles[]와 같은 모양. 소셜 지식을 한 곳에 가두고 나머지는 타입으로만 다룸. 단 ②-4의 L1 매핑 레지스트리 누락 |
BaseModel 일관성 | 적합 | time-series를 포기한 결정 덕에 모든 컬렉션이 buildSchema+timestamps+트랜잭션 규칙을 동일하게 따름 — 예외 컬렉션이 없음 |
polymorphic Mixed | 주의 | 스키마 검증이 사라지는 구간. 레지스트리가 곧 검증기 역할을 해야 하므로 METRIC_SPEC에 없는 키가 들어오면 경고하는 장치가 필요 |
sourceType enum 규모 | 주의 | v1 5종 → v2 14종 → audit 후 20종+. 코드 SocialSourceType은 현재 14종이라 이미 어긋나 있음. enum 확장이 코드 반영 단계의 첫 작업 |
platform vs sourceType 중복 | 정당 | platform은 sourceType에서 파생 가능하지만, 코호트 인덱스({platform, observed_at})를 위한 의도적 비정규화. 단 ②-3의 키 충돌은 별개 문제 |
| 3층(core/L2/data) 유지비 | 낮음 | audit 결과 L2가 얇게 나왔다(소셜당 1~4필드). 대부분 L1에 흡수돼 소셜별 분기 코드가 적음 |
| 단계 | 변경 | 비용 |
|---|---|---|
| 컬렉션 | 없음 — contents/accounts/venues/스냅샷 그대로 | 0 |
| enum | sourceType에 타입 추가 | 소 |
| 매핑 | ContentGenerator + METRIC_SPEC에 항목 1개씩 | 소 |
| L2 | ext.<코드> 네임스페이스 추가 — 다른 소셜과 충돌 없음 | 0 |
| 인덱스 | 필요 시 partial index | 소 |
Venue를 추가하며 이미 한 번 검증됐다 — 컬렉션 1개 신설 + contents.venueKey 참조로 끝났고 기존 구조를 안 건드렸다.
Telegram처럼 Venue만 있는 소셜도 같은 틀에 들어간다(Creator·Content가 비는 것이 자연스러움).
구조가 잘 버티는 건 "또 다른 소셜"이다. 반대로 website처럼 성격이 다른 소스(계정도 콘텐츠도 아니고 도메인 인프라)는 지금 3객체 모델(Creator/Content/Venue)에 깔끔히 안 들어간다 — ②-1이 발생한 구조적 이유이기도 하다.
website는 Venue에 가깝지만(장소성) 멤버·개설자가 없고, Creator에 가깝지만(주체성) 게시물이 없다. → 4번째 객체 Infra(도메인)를 인정하거나, Venue를 "주체가 아닌 귀속점"으로 넓게 재정의해야 한다.
| 초판에서 본 것 | 초판에서 빠졌던 것 |
|---|---|
| 쿼리 31개의 표현 가능성 | 문서 크기 성장 — raw가 붙어 다니는 비용 |
| 인덱스 존재 여부 | 쓰기 패턴 — 같은 콘텐츠 재fetch 시 무엇이 덮이나 |
| 컬렉션 간 홉 수 | SoT 중복 — 같은 사실이 두 곳에 있는가 |
| 참조 갱신 규칙 — 마스터를 언제 갱신하나 | |
| 컬렉션 분할의 실익 — 규모 대비 |
contents.data(무손실 raw)가 문서에 붙어 있다X 트윗은 author 33키 + quoted_tweet 전체가 들어가 문서 하나가 수십 KB가 된다.
MongoDB는 문서 단위 I/O라 목록 조회에서 안 쓰는 raw까지 읽힌다
(projection으로 필드는 뺄 수 있어도 캐시·스토리지 효율은 그대로 손해).
→ content_raw 별도 컬렉션으로 분리. contents는 가벼워지고 raw는 재해석이 필요할 때만 읽는다.
원인: v3 §③이 L3를 "인덱스 없음"이라고만 적고 어디에 사는지는 정하지 않았다.
raw 보존의 가치(audit 내내 반복된 교훈 — 절단된 덤프 때문에 alt·isSlideshow·author.id를 놓쳤다)는 그대로 유지된다.
fetchStatus와 links.status가 같은 사실을 두 곳에 둔다v2 Q6("링크가 살아있나/삭제됐나/애초에 없었나")은 links{observedAt ≤ T} → status로 답한다.
그런데 v3는 contents.fetchStatus도 뒀다 — 어느 쪽이 SoT인지 정하지 않았다.
더 큰 문제: contents.observedAt이 단수다. 재fetch 시 덮어쓰면 ok → deleted 전이를 못 본다.
"지금 상태"만 남고 "언제 죽었나"가 사라진다 — v2의 관측 로그(소급 불가) 원칙과 충돌한다.
→ 상태 이력은 links(또는 스냅샷)에만 두고, contents.fetchStatus는 최신값 캐시임을 명시한다.
sourceType → 객체종류 → 컬렉션 매핑이 없다audit은 소셜을 Creator / Content / Venue 3객체로 정리했고, 라우터는 URL에서 sourceType을 낸다.
그런데 둘의 연결 고리가 v3에 통째로 빠져 있다.
x_profile → Creator → accounts x_tweet → Content → contents x_community → Venue → venues github_owner → Creator → accounts github_repo → Content → contents tiktok_shortlink → ? // 해석 전엔 객체 종류를 모른다
→ sourceType마다 objectType을 선언하고, 그것이 저장 컬렉션을 결정하도록 규칙을 명시해야 한다.
shortlink처럼 해석 전엔 종류를 모르는 타입의 처리(보류 큐 or 해석 후 배정)도 같이 정한다.
core. 래핑의 실익이 약하다초판은 이 문제를 대안 (c)에서 "취향 문제"로 넘겼는데 얕은 판단이었다.
| 기대했던 이득 | 실제 |
|---|---|
| L2와 이름 충돌 방지 | L2는 이미 ext.<코드>로 격리돼 있어 효과 없음 |
| 의미 그룹이 드러남 | 문서 가독성 — 유일한 실익 |
| 비용 | 모든 인덱스·쿼리에 core. prefix + typegoose 중첩 클래스 정의 |
→ 평면 + L2 네임스페이스가 더 단순하다. 되돌리기 쉬운 결정이므로 코드 반영 전에 확정한다.
venues 별도 컬렉션의 실익규모: X community 137 · reddit subreddit 161건. accounts와 필드가 거의 같고
(createdAt·description·metrics·creatorId), 개설자가 accounts를 가리켜 크로스 컬렉션 참조가 생긴다.
초판에서 분리를 정당화한 근거는 "독자 상태를 갖고 여러 콘텐츠가 공유한다"였는데, 그건 accounts도 마찬가지라 분리 사유가 되지 못한다.
→ accounts에 kind: user | org | venue로 통합하면 참조가 같은 컬렉션 안에서 닫힌다.
단 관계는 분명히 다르다 — Creator는 authored-by, Venue는 posted-in.
관계가 다르니 필드·인덱스는 분리하되, 컬렉션까지 나눌 필요가 있나는 별개 질문이고 이 규모에선 아닐 수 있다.
| # | 항목 | 내용 |
|---|---|---|
| ⑤-6 | accounts 마스터 갱신 규칙 부재 |
contents.author{} 스냅샷이 콘텐츠마다 쌓인다(같은 계정이 1000개 콘텐츠에 나오면 1000번).
as-of 정확성 때문에 중복 저장은 의도된 것이지만, 언제 accounts 마스터를 갱신하는지 규칙이 없다.
→ "더 최신 observedAt이면 갱신" 같은 규칙 필요 |
| ⑤-7 | 3홉이 v1 원칙과 어긋남 | v1은 "1~2홉 이내에서 $lookup 배제"를 원칙으로 세웠는데,
A-3(계정→포트폴리오)은 accounts → contents → links → tokens로 3홉이다.
as-of 요구(배열→엣지 전환) 때문에 불가피하나 원칙과 실제가 어긋난 상태가 기록돼 있지 않다 |
| 관점 | 상태 |
|---|---|
| 쿼리 표현력 | 확인됨 31개 중 19 지원, 불가 7개 중 4개는 의도된 폐기 |
| 모델링 건전성 | 결함 3건 raw 분리 · SoT 중복 · sourceType 매핑 |
| 성능 | 판단 불가 실데이터 카디널리티·문서 크기 분포·인덱스 선택도 미측정 |
"최적"을 주장하려면 최소한 실데이터로 문서 크기 분포와 주요 쿼리 explain을 봐야 하는데, 그건 코드 반영 이후에나 가능하다.
| 대안 | 장점 | 기각/채택 사유 |
|---|---|---|
(a) 3객체를 social_entities 하나로 통합entityType: creator|content|venue |
"이 토큰에 붙은 모든 소셜 엔티티"가 1회 조회. 소셜마다 있는 객체가 달라도(TG=venue only) 균일 | 전면 통합은 기각 — 라이프사이클이 다르다. creator는 재사용(여러 콘텐츠가 공유)이고 content는 1회성이다.
무엇보다 Q3·A-3 같은 계정 단위 질문이 accounts를 독립 주체로 요구한다. 단 부분 통합은 검토 — accounts + venues만 kind로 합치는 안은 유효하다(§⑤-5).
규모가 작고(venue 300건 수준) 필드가 유사하며 개설자 참조가 같은 컬렉션 안에서 닫힌다 |
| (b) 엣지를 배열로 되돌리기 tokens.contentRefs[] |
홉 1개 감소 | 기각 — v2가 이미 기각한 이유가 유효: 배열 원소에 시각이 없어 as-of 절단(Q1·Q2)이 불가능. 그게 이 도메인의 핵심 요구 |
| (c) core를 flatten text_primary 등 평면 키 |
모든 인덱스·쿼리의 core. prefix 제거 + typegoose 중첩 클래스 불필요 |
판정 수정 · 채택 검토 — 초판은 "취향 문제"로 넘겼으나 §⑤-4에서 재검토한 결과
기대했던 이득(L2와 이름 충돌 방지)이 실제로는 없다(L2가 이미 ext.<코드>로 격리됨).
남는 실익은 문서 가독성뿐이고 비용은 계속 든다 → 평면 + L2 네임스페이스가 더 단순 |
| (d) 4번째 객체 Infra 신설 domain/website 계열 |
②-1을 구조적으로 해소. fingerprint·RDAP·cert가 자기 자리를 가짐 |
검토 권장 — 유일하게 지금 구조를 실제로 개선하는 대안. 다만 website audit이 선행돼야 필드 목록이 나온다 |
| # | 결정 | v3 위치 |
|---|---|---|
| 1 | content_raw 별도 컬렉션 — _id(=contentKey)만 두고 보조 인덱스 없음(id 로만 조회) | §③ L3 |
| 2 | 상태 이력의 SoT 는 links. contents.fetchStatus 는 최신값 캐시로 명시 | §⑧ |
| 3 | SOURCE_TYPE_OBJECT(상수 맵, objectType 분류) + ContentGenerator(_id 추출·fan-out) — C 안 | §⑩ |
| 4 | platform(구분 문자열) + ext(L2 네임스페이스)로 키 분리 | §③·§⑤·§⑦ |
3번에서 기각한 대안 — 상수 맵 하나로 전부: fan-out 구조를 표현 못 함. 각 fetcher 가 선언: fetcher 가 "가져오기 + 변환" 두 일을 하고, 지표만 레지스트리로 모으는 비일관.
| # | 조치 | 이유 | 출처 |
|---|---|---|---|
| 1 | content_raw 분리 |
무손실 raw가 contents에 붙어 있어 목록 조회마다 수십 KB를 같이 읽는다. 나중에 옮기면 전량 마이그레이션이 필요 | §⑤-1 |
| 2 | 상태 SoT 확정 fetchStatus vs links.status |
같은 사실이 두 곳에 있고 observedAt이 단수라 ok → deleted 전이가 소실된다 — v2 관측로그 원칙 위반 | §⑤-2 |
| 3 | sourceType → objectType → 컬렉션 매핑 정의 |
라우터 산출과 3객체 모델의 연결 고리가 통째로 없다. shortlink처럼 해석 전엔 종류를 모르는 타입 처리도 포함 | §⑤-3 |
| 4 | platform 키 충돌 수정 |
한 문서에 같은 키가 두 의미로 존재 — 그대로 옮기면 즉시 데이터 손실 | §②-3 |
| # | 조치 | 이유 | 출처 |
|---|---|---|---|
| 5 | core. 유지 vs 평면화 | 기대했던 이득이 없음이 확인됨. 되돌리기 쉬우나 코드 반영 후엔 비싸진다 | §⑤-4 |
| 6 | venues 별도 vs accounts 통합 | 규모 300건 · 필드 유사 · 크로스 컬렉션 참조 발생 | §⑤-5 |
| 7 | ContentGenerator 명시 | 지표만 레지스트리로 모으고 L1 매핑은 표로만 둔 비일관 | §②-4 |
| 8 | accounts 갱신 규칙 | author 스냅샷은 쌓이는데 마스터를 언제 갱신하는지 미정 | §⑤-6 |
| # | 조치 | 이유 | 출처 |
|---|---|---|---|
| 9 | "폐기된 쿼리" 표 추가 | Category B 3개 + §5.5가 조용히 죽어 있음. v1은 여전히 B를 "가장 가치 높은"이라 말함 | §②-5 |
| 10 | Q10 처리 결정 URL만 담기 vs 명시 보류 | 지금은 "조용히 불가"라 최악 | §②-2 |
| 11 | 3홉 현황 기록 | v1 "1~2홉" 원칙과 실제가 어긋난 상태가 미기록 | §⑤-7 |
인덱스는 구조를 바꾸지 않는 순수 추가라 코드 반영 시점에 정해도 된다. 다만 조사 중 나온 항목이라 잊지 않도록 기록만 남긴다.
| 항목 | 내용 |
|---|---|
content_token_links의 tokenAddress 진입 |
v2가 명시한 건 (contentKey, linkedAt) 하나인데 반대 방향 진입이 여럿이다 —
A-1(토큰→콘텐츠) · Q6(links{tokenAddress=Y, observedAt ≤ T}) · Q7(코호트 구성).
links는 핫 경로(Q1·Q2·A-1·A-2·Q6·Q7·Q8이 전부 여기를 침)라 (tokenAddress, linkedAt)가 없으면 collscan |
contents 인덱스 조합 미정 |
접근 패턴이 최소 4가지 — creatorId+publishedAt(Q3) · platform+publishedAt(코호트) ·
sourceType · genealogy.parentContentId(계보 재귀) |
A-3의 $in 폭발 |
계정이 콘텐츠 1000개를 쓰면 links 조회가 1000키 $in. 3홉 구조상 불가피하므로 페이징·청킹 규칙을 정해야 함 |
| 문제 아님(확인) | 스냅샷의 {platform, observed_at}은 등가+범위라 ESR 규칙상 올바른 순서다.
단 플랫폼 무관 시간 범위 스캔이 필요하면 {observed_at}이 별도로 필요 |
| # | 조치 | 이유 | 출처 |
|---|---|---|---|
| 12 | website 소셜 audit → Infra 객체 검토 | Q4·§5.4의 유일한 공급원. 쿼리 관점 최대 갭이지만 audit 1회가 선행돼야 필드 목록이 나옴 | §②-1 · §⑥(d) |
| 13 | telegram audit | Venue 전용 소셜의 요구사항이 아직 미반영 | v3 §⑩ |
| 14 | 미정 3건 확정 관측 주기 · 백분위 코호트 · entities 라벨 생성 |
조건부 5개(C-1·D-1·D-3·5.1-a·Q9)가 전부 여기 걸림. 실험이 필요하지 결정으로 풀 수 없음 | v3 §⑩ |
| 15 | 실데이터 성능 측정 | "최적" 주장의 전제 — 문서 크기 분포 + 주요 쿼리 explain. 코드 반영 이후에만 가능 | §⑤ 결론 |
website.json이 없어 관측량조차 모른다(다른 13종은 3~3879건).