그래프 데이터베이스 — Neo4j 와 Cypher · 순회와 경로 · 퀴즈
퀴즈: 순회와 경로
문항 8개. 정답과 해설은 풀어 본 뒤에 보여 드립니다.
`MATCH (a:Account {id: 8})-[:TRANSFER*1..3]->(b)` 에서 `count(b)` 는 33, `count(DISTINCT b)` 는 32 였다. 이 차이가 뜻하는 것은?
- 송금 한 건이 두 번 적재돼 관계가 중복돼 있다
- 어떤 계좌 하나에 서로 다른 두 경로로 닿았다 — 앞은 경로 수, 뒤는 계좌 수다
- 3걸음째의 계좌 하나가 출발 계좌 자신이라 DISTINCT 에서 빠졌다
- count(b) 가 NULL 인 행을 함께 세어 하나 더 나온 것이다
`shortestPath((p1)-[*..8]-(p2))` 가 돌려주는 것은?
- p1 과 p2 사이의 모든 경로를 길이 순으로 정렬한 목록
- 길이 8 인 경로 중 관계 속성 합이 가장 작은 것
- 가장 짧은 경로가 여럿이면 그 전부
- 가장 짧은 경로 하나 — 같은 길이가 여럿이면 어느 것인지는 정해져 있지 않다
계좌 a 에 대해 `(a)-[t:TRANSFER]-()` 로 `count(t)` 를 세면 무엇이 나오나?
- a 가 보낸 송금 수와 받은 송금 수를 더한 값
- a 가 보낸 송금 수만 — 방향을 생략하면 나가는 쪽이 기본이다
- a 와 이어진 서로 다른 계좌의 수
- a 가 보낸 것과 받은 것 중 더 큰 쪽
`MATCH (p:Person)-[:USES]->(d:Device) RETURN p, d` 결과에 기기가 없는 사람은 어떻게 나오나?
- d 가 NULL 인 행으로 나온다
- p 만 있고 d 열이 비어 있는 행으로 나온다
- 오류가 나서 질의 전체가 실패한다
- 아예 나오지 않는다 — 패턴이 맞지 않으면 그 사람은 결과에서 사라진다
`WITH d, count(DISTINCT p) AS users WHERE users >= 4` 에서 `WITH` 가 하는 일은?
- 집계한 값에 이름을 붙여 다음 절로 넘기고, 그 값으로 거르거나 정렬할 수 있게 경계를 만든다
- d 와 p 를 하나의 노드로 합쳐 새 패턴을 만든다
- 결과를 임시 표에 저장해 질의가 끝난 뒤에도 남긴다
- 트랜잭션을 커밋하고 새 트랜잭션을 연다
`PROFILE MATCH (p:Person {email: 'x'}) RETURN p` 에 `NodeByLabelScan` 과 db hits 4002 가 찍혔다. 사람은 2,000명이다. 무엇을 뜻하나?
- 저장소가 손상돼 노드마다 레코드를 두 번씩 읽고 있으니 일관성 검사를 돌려야 한다
- email 값이 중복돼 같은 노드가 여러 번 매칭됐으니 유일성 제약을 걸어야 한다
- 이메일 인덱스가 없어 Person 전체를 훑고 각각의 속성을 읽었다 — 시작점은 인덱스가 찾아야 한다
- 페이지 캐시가 비어 있어 디스크에서 두 번 읽은 것이니 질의를 다시 돌리면 줄어든다
가변 길이 패턴에 `*..5` 처럼 상한을 두라고 하는 이유는?
- 상한이 없으면 파서가 길이를 정하지 못해 Cypher 문법 오류로 거부되기 때문
- 촘촘한 그래프에서는 걸음마다 경로 수가 곱으로 늘어 메모리와 시간이 먼저 바닥나기 때문
- 상한이 없으면 플래너가 shortestPath 로 바꿔 버려 모든 경로를 돌려주지 않기 때문
- 관계 종류를 지정한 패턴에서는 길이 범위를 함께 적어야 종류가 적용되기 때문
송금이 서로 얽혀 순환이 있는 그래프에서 `-[:TRANSFER*1..5]->` 가 무한히 돌지 않는 이유는?
- Neo4j 가 순환을 감지하면 그 노드를 결과에서 제외하기 때문
- 가변 길이 패턴은 같은 노드를 두 번 지나가지 않기 때문
- 한 경로 안에서 같은 관계를 두 번 밟지 않기 때문 — 노드는 다시 지날 수 있어도 관계는 한 번이다
- 순환은 별도의 알고리즘으로만 찾을 수 있고 MATCH 는 순환을 걷지 못하기 때문