그래프 데이터베이스 — Neo4j 와 Cypher · 그래프가 이기는 문제 · 실습
그래프가 이기는 문제 — 사기 고리, 순환 송금, 허브, 추천
목표
같은 그래프에서 네 문제를 풉니다 — 기기와 주소를 함께 쓰는 사기 고리, 고액으로만 돌아
나오는 순환 송금, 차수가 유난히 높은 허브, 그리고 협업 필터링 추천. 끝에 조사 보고서
한 장으로 묶습니다.
왜 중요한가
넷 다 답이 한 행에 있지 않고 행들 사이에 있습니다. 표로 풀면 자기 조인이 쌓이고,
그래프로 풀면 패턴 한 줄입니다. 그런데 넷 다 그럴듯한 오답이 있습니다 — 기기만 보면
공용 단말이, 주소만 보면 가족이, 금액 없이 보면 밥값 돌려주기가, 차수만 보면 급여
계좌가 잡힙니다. 패턴을 좁혀 오답을 떼어 내는 것이 이 실습의 과제이고, 채점기는
픽스처에 심어 둔 정답과 살아 있는 그래프에서 다시 뽑은 값을 함께 맞춥니다.
환경
이 파드는 postgres 계정으로 돕니다. Neo4j 5.26 Community 가 /opt/neo4j 에 있고
기본으로 꺼져 있습니다. 1단계에서 직접 켭니다.
/opt/lab/engines.sh start neo4j # 15~30초 걸립니다export PATH=/opt/neo4j/bin:$PATHcypher-shell -u neo4j -p labhub # 대화형. 문장은 ; 로 끝냅니다. :exit 로 나갑니다cypher-shell -u neo4j -p labhub --format plain "MATCH (n) RETURN count(n);" # 한 줄 실행--format plain 은 첫 줄에 열 이름, 그다음 줄부터 값을 냅니다. 문자열은 큰따옴표로
감싸 나오고 숫자는 그대로 나옵니다. 파일로 남길 때 이 출력을 그대로 써도 됩니다.
픽스처 CSV 는 /opt/lab/fixtures/graph/ 에 있고, Cypher 의 `LOAD CSV WITH HEADERS
FROM 'file:///people.csv'` 가 그 디렉터리로 풀립니다. 읽기 전용이라 생각하고,
산출물은 전부 /root/graph/ 아래에 둡니다.
JSON 산출물은 파이썬 드라이버(neo4j 패키지가 설치돼 있습니다)로 쓰는 것이 쉽습니다.record.data() 가 RETURN 의 별칭을 키로 하는 딕셔너리를 줍니다.
import jsonfrom neo4j import GraphDatabased = GraphDatabase.driver("bolt://127.0.0.1:7687", auth=("neo4j", "labhub"))rows = d.execute_query("MATCH (a:Account) RETURN a.id AS account LIMIT 3").recordsjson.dump([r.data() for r in rows], open("/root/graph/fraud/x.json", "w"))이 실습은 실습 1·2 의 결과에 기대지 않습니다. 1단계의 reload.cypher 가 같은 그래프를
처음부터 다시 세웁니다.
단계
1. Neo4j 를 켜고 /opt/lab/fixtures/graph/reload.cypher 로 그래프를 세운 뒤 /root/graph/fraud/01-counts.txt 에 nodes= · rels= 를 남깁니다.
2. 네 사람 이상이 쓰는 기기를 모두 찾아 /root/graph/fraud/02-shared.txt 에 shared_devices= 와 기기마다 device=<id> users=<n> 을 남깁니다.
3. 같은 기기를 쓰면서 같은 주소에 사는 넷 이상의 무리를 /root/graph/fraud/ring.json 에 device · address · members 객체의 배열로 남깁니다.
4. 고리 구성원 전체의 members= · accounts= · balance= · internal_transfers= 를 /root/graph/fraud/04-ring-money.txt 에 남깁니다.
5. 500,000 이상 송금만 따라 3~5단계에 돌아오는 순환을 /root/graph/fraud/cycles.json 에 계좌 id 배열의 배열로 남깁니다.
6. 송금 차수 상위 5개 계좌를 /root/graph/fraud/hubs.json 에 account · degree 객체 다섯 개로 남깁니다.
7. 사람 27 에게 추천할 상품 다섯을 /root/graph/fraud/recs.json 에 product · score 객체 다섯 개로 남깁니다.
8. /root/graph/fraud/report.md 에 고리·순환·허브·추천 네 절을 둔 보고서를 씁니다.
참고
- 집계한 값으로 거르려면
WITH ... WHERE입니다.MATCH ... WHERE count(...)는 되지 않습니다. - 순환은 출발 노드마다 한 번씩 반복해 나옵니다.
all(n IN nodes(p) WHERE n.id >= a.id)로 하나만 남깁니다. count(DISTINCT other)와count(other)는 추천 점수를 다르게 만듭니다. 같은 사람이 여러 상품으로 겹치기 때문입니다.- 허브 1위는 사기가 아닐 수 있습니다. 차수는 후보를 뽑는 것이고 판단은 방향과 상대가 합니다.
단계 8개
- Neo4j 를 켜고 그래프를 다시 세운다
- 네 사람 이상이 같이 쓰는 기기
- 사기 고리 — 기기와 주소를 둘 다 함께 쓰는 무리
- 고리의 돈
- 순환 송금 — 돌아 나오는 돈
- 허브 — 차수가 가장 높은 계좌
- 협업 필터링 — 나와 같은 것을 산 사람이 산 것
- 조사 보고서 한 장