DOCS/troubleshooting/251016_neo4j_startup_data_migration.md
happybell80 910e4ea6b7 docs: Neo4j 스타트업 데이터 마이그레이션 작업 문서화
- YC 5,490개 + 한국 12,703개 + VC 60개 + 뉴스 7,479개
- 총 26,117 노드, 27,227 관계 생성
- 그래프 쿼리 예시 5가지 포함
- 데이터 구조 설계 및 제약 조건 명시
- 발견된 오류 4건 및 해결 방법 기록

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 23:12:10 +09:00

404 lines
9.5 KiB
Markdown

# Neo4j 스타트업 데이터 마이그레이션
**날짜**: 2025-10-16
**작성자**: Claude (51123 서버 관리자)
**작업 시간**: 21:35 - 23:00 KST
**분류**: 데이터 마이그레이션
---
## 작업 개요
스타트업 생태계 분석을 위해 다양한 데이터를 Neo4j 그래프 데이터베이스로 통합 마이그레이션
**데이터 소스**:
- YC 기업: 5,490개 (yc_companies_all.json)
- 한국 스타트업: 12,703개 (startup_data_20251016.json)
- VC firms: 60개 (vc_firms_connor.csv)
- 뉴스 기사: 7,479개 (Platum, Outstanding 중복 제거 후)
**최종 결과**:
- 총 노드: 26,117개
- 총 관계: 27,227개
- DB 크기: 532MB (HDD 저장)
---
## 데이터 구조 설계
### 노드 타입
1. **Startup** (18,193개)
- YC 기업: 5,490개
- 한국 스타트업: 12,703개
- 속성: id, name, description, website, revenue, source 등
2. **VC** (60개)
- 속성: id, name, website, city, state, description
3. **Batch** (45개)
- YC 배치 정보 (W24, S24 등)
4. **Category** (9개)
- 산업 분류 (industry type)
5. **Tag** (331개)
- 기술 스택, 비즈니스 모델 등
6. **NewsArticle** (7,479개)
- 출처: Platum, Outstanding
- 속성: url, title, source, date
### 관계 타입
1. **BATCH** (5,490개)
- Startup → Batch
2. **BELONGS_TO** (5,490개)
- Startup → Category
3. **TAGGED** (14,714개)
- Startup → Tag
4. **MENTIONS** (1,533개)
- NewsArticle → Startup
- 뉴스 제목에 기업명 포함 시 생성
---
## 기술 스택
### Neo4j 연결
```python
from neo4j import GraphDatabase
NEO4J_URI = "bolt://localhost:7687"
NEO4J_USER = "neo4j"
NEO4J_PASSWORD = "robeing2025!"
```
### 제약 조건
```cypher
CREATE CONSTRAINT startup_id IF NOT EXISTS FOR (s:Startup) REQUIRE s.id IS UNIQUE
CREATE CONSTRAINT vc_id IF NOT EXISTS FOR (v:VC) REQUIRE v.id IS UNIQUE
CREATE CONSTRAINT batch_name IF NOT EXISTS FOR (b:Batch) REQUIRE b.name IS UNIQUE
CREATE CONSTRAINT category_name IF NOT EXISTS FOR (c:Category) REQUIRE c.name IS UNIQUE
CREATE CONSTRAINT news_url IF NOT EXISTS FOR (n:NewsArticle) REQUIRE n.url IS UNIQUE
```
---
## 데이터 로딩 과정
### Phase 1: YC 기업 (5,490개)
**파일**: `/mnt/hdd/data/DATA/startup/yc_companies_all.json`
**로직**:
1. Startup 노드 생성 (MERGE)
2. Batch 노드 생성 및 관계 연결
3. Category 노드 생성 및 관계 연결
4. Tag 노드 생성 및 관계 연결 (tags 배열)
**배치 처리**: 1,000개 단위로 진행 표시
---
### Phase 2: 한국 스타트업 (12,703개)
**파일**: `/mnt/hdd/data/DATA/startup/data/startup_data_20251016.json`
**초기 시도**: startup_data.json (2,870개) 사용
**업그레이드**: startup_data_20251016.json으로 변경 (최신 데이터)
**속성**:
- corpId (고유 ID)
- corpNameKr, corpNameEn (한글/영문 이름)
- corpIntroKr (기업 소개)
- corpLogoImg (로고 URL)
- finacRevenueVal (매출액)
---
### Phase 3: VC Firms (60개)
**파일**: `/mnt/hdd/data/DATA/startup/vc_firms_connor.csv`
**속성**:
- Employbl Company ID (고유 ID)
- Company Name
- Website
- City, State
- Company Description
---
### Phase 4: 뉴스 기사 (7,479개)
**파일 구조 문제 발견**:
```json
// 잘못된 가정: articles가 최상위 배열
[{"url": "...", "title": "..."}]
// 실제 구조: articles가 중첩 객체
{"articles": [{"url": "...", "title": "..."}]}
```
**해결**:
```python
# Before
articles = list(data.values()) if isinstance(data, dict) else data
# After
articles = data.get('articles', [])
```
**데이터 소스**:
1. Platum: `/mnt/hdd/data/DATA/startup_crawl/platum/articles_*.json`
2. Outstanding: `/mnt/hdd/data/DATA/startup_crawl/outstanding/articles_*.json`
**중복 제거**: MERGE (url 기준)으로 33,225건 → 7,479건
---
### Phase 5: 뉴스-스타트업 관계 생성
**쿼리**:
```cypher
MATCH (s:Startup), (n:NewsArticle)
WHERE s.name IS NOT NULL
AND n.title CONTAINS s.name
AND size(s.name) > 2
MERGE (n)-[:MENTIONS]->(s)
```
**오류 수정**:
- `length(s.name)``size(s.name)` (Neo4j 함수 호환성)
**결과**: 1,533개 MENTIONS 관계 생성
---
## 구현 파일
### 1. `/mnt/hdd/data/DATA/.env`
```bash
NEO4J_URI=bolt://localhost:7687
NEO4J_USER=neo4j
NEO4J_PASSWORD=robeing2025!
```
### 2. `/mnt/hdd/data/DATA/load_to_neo4j.py`
- 초기 버전 (YC + 한국 기업 + VC)
- 한국 스타트업 2,870개 → 12,703개 업그레이드
### 3. `/mnt/hdd/data/DATA/load_all_to_neo4j.py`
- 완전판 (뉴스 기사 포함)
- 클래스 구조: `FullNeo4jLoader`
- 메서드:
- `clear_database()`: 초기화
- `create_constraints()`: 인덱스 생성
- `load_basic_data()`: YC, 한국, VC 로드
- `load_news()`: 뉴스 기사 로드
- `link_news_to_startups()`: 관계 생성
- `print_stats()`: 통계 출력
---
## 발견된 오류 및 해결
### Error 1: Neo4j 인증 실패
```
Neo.ClientError.Security.Unauthorized
```
**해결**: DOCS에서 비밀번호 검색
```bash
grep -r "neo4j" /home/admin/DOCS/
# robeing2025! 발견
```
---
### Error 2: 뉴스 데이터 0건
**원인**: JSON 구조 잘못된 가정 (articles가 중첩됨)
**해결**: `data.get('articles', [])` 사용
---
### Error 3: Neo4j length() 타입 에러
```
Type mismatch: expected Path but was Boolean, Float, Integer...
```
**원인**: `length()` 함수는 경로 길이 전용, 문자열은 `size()` 사용
**해결**: `length(s.name)``size(s.name)`
---
### Error 4: 스크립트 클래스 구조 깨짐
**원인**: Shell append로 메서드 추가 시 클래스 밖에 추가됨
**해결**: 완전한 새 파일 `load_all_to_neo4j.py` 생성
---
## 최종 통계
### 노드 분포
| 노드 타입 | 개수 | 비율 |
|----------|------|------|
| Startup | 18,193 | 69.6% |
| NewsArticle | 7,479 | 28.6% |
| Tag | 331 | 1.3% |
| VC | 60 | 0.2% |
| Batch | 45 | 0.2% |
| Category | 9 | 0.0% |
| **합계** | **26,117** | **100%** |
### 관계 분포
| 관계 타입 | 개수 | 비율 |
|----------|------|------|
| TAGGED | 14,714 | 54.0% |
| BATCH | 5,490 | 20.2% |
| BELONGS_TO | 5,490 | 20.2% |
| MENTIONS | 1,533 | 5.6% |
| **합계** | **27,227** | **100%** |
---
## 활용 예시
### 1. AI 스타트업 추천
```cypher
MATCH (s:Startup)-[:TAGGED]->(t:Tag)
WHERE toLower(t.name) CONTAINS 'ai'
AND s.source = 'YC'
RETURN s.name, s.website, s.description
LIMIT 10
```
### 2. 한국 Fintech 기업 (매출 순위)
```cypher
MATCH (s:Startup)-[:TAGGED]->(t:Tag)
WHERE s.source = 'Korea'
AND toLower(t.name) CONTAINS 'fintech'
AND s.revenue > 0
RETURN s.name, s.revenue
ORDER BY s.revenue DESC
LIMIT 20
```
### 3. B2B SaaS 기업 발굴
```cypher
MATCH (s:Startup)-[:TAGGED]->(t:Tag)
WHERE toLower(t.name) IN ['saas', 'b2b', 'enterprise']
RETURN s.name, s.website, s.description, collect(t.name) as tags
LIMIT 15
```
### 4. 뉴스 버즈 기업 분석
```cypher
MATCH (n:NewsArticle)-[:MENTIONS]->(s:Startup)
RETURN s.name, count(n) as mention_count, collect(n.source) as sources
ORDER BY mention_count DESC
LIMIT 10
```
### 5. 유사 기업 찾기 (태그 기반)
```cypher
MATCH (target:Startup {name: '리버스마운틴'})-[:TAGGED]->(t:Tag)<-[:TAGGED]-(similar:Startup)
WHERE target <> similar
RETURN similar.name, similar.description, count(t) as common_tags
ORDER BY common_tags DESC
LIMIT 10
```
---
## 검증 테스트
### 리버스마운틴 조회
```cypher
MATCH (s:Startup)
WHERE s.name CONTAINS '리버스마운틴'
RETURN s.name, s.name_en, s.description, s.revenue
```
**결과**:
- 이름: 리버스마운틴
- 영문명: RI BUS MAUNTIN CO.,LTD.
- 서비스: 티키타카 (AI 기반 목표/업무 관리)
- 매출: 0.1억원
---
## 향후 계획
### 1. API 개발
- Neo4j 그래프 쿼리를 REST API로 제공
- robeing-gateway 또는 새 마이크로서비스
### 2. 추가 데이터 연결
- 12,688개 마크다운 파일 구조화
- 투자자 정보 추가 (Co-investment 분석)
### 3. 시계열 분석
- 2024-11-27 → 2025-10-16 변화 추적
- 생존율/폐업률 모니터링
### 4. 고급 그래프 분석
- PageRank: 영향력 있는 기업 발굴
- Community Detection: 산업 클러스터 분석
- Shortest Path: 기업 간 연결 경로
---
## 참고 자료
### 데이터 파일 위치
- YC: `/mnt/hdd/data/DATA/startup/yc_companies_all.json`
- 한국: `/mnt/hdd/data/DATA/startup/data/startup_data_20251016.json`
- VC: `/mnt/hdd/data/DATA/startup/vc_firms_connor.csv`
- 뉴스: `/mnt/hdd/data/DATA/startup_crawl/{platum,outstanding}/*.json`
### 스크립트 위치
- 초기판: `/mnt/hdd/data/DATA/load_to_neo4j.py`
- 완전판: `/mnt/hdd/data/DATA/load_all_to_neo4j.py`
- 환경변수: `/mnt/hdd/data/DATA/.env`
### Neo4j 저장소
- 물리 경로: `/mnt/hdd/neo4j/data/` (HDD)
- 심볼릭 링크: `/var/lib/neo4j/data`
- 크기: 532MB
### 관련 문서
- 트렌드 분석: `/home/admin/DOCS/troubleshooting/251016_startup_trend_analysis.md`
- 인프라 작업: `/home/admin/DOCS/troubleshooting/251016_troubleshooting_summary.md`
---
## 교훈
### 데이터 구조 검증
실제 JSON 파일을 직접 확인해야 함. 중첩 구조 가능성 항상 고려.
### Neo4j 함수 호환성
- 문자열 길이: `size()` 사용
- 경로 길이: `length()` 사용
- 문서 참조 필수
### 배치 처리 필요성
18,193개 스타트업 로드 시 1,000개 단위로 진행 표시 → 사용자 불안감 해소
### 데이터 품질
- 한국 데이터: 투자 단계 57.8% 미공개
- 뉴스 데이터: URL 중복 제거로 33,225 → 7,479
- 관계 생성: 이름 매칭 단순 방식 (2글자 이하 제외)
### 그래프 DB 장점
- 유사 기업 찾기: JOIN 없이 패턴 매칭
- 뉴스 버즈 분석: 관계 카운팅 간단
- 투자 네트워크: 미래 확장 용이