40

고급 Cypher: 경로 탐색과 영향도 분석

Neo4j & Cypher

학습 목표

가변 길이 경로 탐색(*1..N)을 수행한다 shortestPath로 최단 경로를 찾는다 집계 함수(COUNT, COLLECT, REDUCE)를 활용한다 제조 현장의 영향도 분석 쿼리를 작성한다

고급 Cypher: "이 설비가 멈추면 무슨 일이 벌어지나?"

1. 가변 길이 경로 탐색

// 알람에서 최종 조치까지 전체 경로 (깊이 1~5)
MATCH path = (f:Fault {alarmCode: "E-201"})
             -[:CAUSED_BY|RESOLVED_BY*1..5]->(end)
WHERE NOT (end)-->()  // 리프 노드까지
RETURN path;

// 설비에서 도달 가능한 모든 노드 (3홉 이내)
MATCH path = (e:Equipment {name: "CNC밀링 3호기"})-[*1..3]->(connected)
RETURN DISTINCT labels(connected)[0] AS 타입,
       connected.name AS 이름,
       length(path) AS 거리;

// 고장 원인의 깊이별 탐색
MATCH (f:Fault {alarmCode: "E-201"})-[:CAUSED_BY*1..3]->(cause)
RETURN cause.name AS 원인,
       length(shortestPath((f)-[:CAUSED_BY*]->(cause))) AS 깊이;

2. 영향도 분석: "이 설비가 멈추면?"

// 설비 정지 시 영향받는 공정 파악
MATCH (e:Equipment {name: "CNC밀링 3호기"})
      <-[:USES_EQUIPMENT]-(process:Process)
OPTIONAL MATCH (process)-[:NEXT_PROCESS*1..5]->(downstream:Process)
RETURN e.name AS 정지설비,
       process.name AS 직접영향_공정,
       collect(DISTINCT downstream.name) AS 후속영향_공정;

// 부품 교체 시 필요한 인력과 시간 산정
MATCH (p:Part {partNumber: "BRG-6205-2RS"})
      -[:CAN_CAUSE]->(f:Fault)
      -[:CAUSED_BY]->(cause:RootCause)
      -[:RESOLVED_BY]->(action:MaintenanceAction)
      -[:REQUIRES_SKILL]->(skill:Skill)
OPTIONAL MATCH (w:Worker)-[:HAS_SKILL]->(skill)
RETURN p.name AS 부품,
       f.name AS 관련고장,
       action.name AS 정비조치,
       action.estimatedTime AS 소요시간,
       skill.name AS 필요스킬,
       collect(w.name) AS 가용인력;

3. 집계와 통계

// 설비별 등록된 부품 수와 고장 위험 부품 수
MATCH (e:Equipment)-[:HAS_COMPONENT]->()-[:HAS_PART]->(p:Part)
WITH e, count(p) AS totalParts,
     sum(CASE WHEN toFloat(p.currentHours)/p.lifespan > 0.8 THEN 1 ELSE 0 END) AS riskParts
RETURN e.name AS 설비,
       totalParts AS 총부품수,
       riskParts AS 위험부품수,
       round(toFloat(riskParts)/totalParts * 100, 1) + "%" AS 위험비율
ORDER BY riskParts DESC;

// 정비 난이도별 조치 목록
MATCH (a:MaintenanceAction)
WITH a.difficulty AS difficulty, collect(a.name) AS actions, count(a) AS cnt
RETURN difficulty AS 난이도,
       cnt AS 조치수,
       actions AS 조치목록
ORDER BY CASE difficulty WHEN "쉬움" THEN 1 WHEN "보통" THEN 2 ELSE 3 END;

// 작업자별 대응 가능한 고장 유형 수
MATCH (w:Worker)-[:HAS_SKILL]->(s:Skill)
      <-[:REQUIRES_SKILL]-(a:MaintenanceAction)
      <-[:RESOLVED_BY]-()-[:CAUSED_BY]-(f:Fault)
RETURN w.name AS 작업자,
       w.experience AS 경력,
       count(DISTINCT f) AS 대응가능_고장수,
       collect(DISTINCT f.alarmCode) AS 대응가능_알람;

4. 실전 분석 쿼리 모음

// Q1: "알람 E-201이 떴는데 뭐부터 확인하나요?"
// → 확률 높은 순으로 원인-조치-필요인력 안내
MATCH (f:Fault {alarmCode: "E-201"})
      -[:CAUSED_BY]->(cause:RootCause)
      -[:RESOLVED_BY]->(action:MaintenanceAction)
      -[:REQUIRES_SKILL]->(skill:Skill)
OPTIONAL MATCH (w:Worker)-[:HAS_SKILL]->(skill)
RETURN cause.name AS 원인,
       cause.probability AS 확률,
       action.name AS 조치,
       action.estimatedTime + "분" AS 소요시간,
       skill.name AS 필요스킬,
       collect(w.name) AS 가용인력
ORDER BY cause.probability DESC;

// Q2: "다음 달 교체 예정 부품은?"
MATCH (e:Equipment)-[:HAS_COMPONENT]->()-[:HAS_PART]->(p:Part)
WHERE toFloat(p.currentHours) / p.lifespan > 0.8
RETURN e.name AS 설비, p.name AS 부품, p.partNumber AS 부품번호,
       p.currentHours AS 현재시간, p.lifespan AS 수명,
       round(toFloat(p.currentHours)/p.lifespan*100, 1) + "%" AS 사용률,
       p.cost AS 비용, p.manufacturer AS 제조사
ORDER BY toFloat(p.currentHours)/p.lifespan DESC;

// Q3: "김기술이 휴가가면 대응 못하는 고장은?"
MATCH (kim:Worker {name: "김기술"})-[:HAS_SKILL]->(kimSkill:Skill)
WITH collect(kimSkill) AS kimSkills
MATCH (f:Fault)-[:CAUSED_BY]->()-[:RESOLVED_BY]->(a:MaintenanceAction)
      -[:REQUIRES_SKILL]->(required:Skill)
WHERE NOT required IN kimSkills
AND NOT EXISTS {
  MATCH (other:Worker)-[:HAS_SKILL]->(required)
  WHERE other.name <> "김기술"
}
RETURN DISTINCT f.alarmCode AS 알람, f.name AS 고장,
       required.name AS 필요스킬,
       "대응 인력 없음 (김기술 부재 시)" AS 상태;

4. 자주 빠뜨리는 핵심 4가지 (실무 직결)

4-1. WITH 절 — 쿼리 체이닝의 핵심

WITH 가 없으면 복잡한 쿼리는 사실상 불가능하다. 중간 결과를 다음 절에 전달하는 "파이프" 역할.

// 설비별 부품 수 → 5개 이상인 설비만 → 정렬
MATCH (e:Equipment)-[:HAS_COMPONENT]->()-[:HAS_PART]->(p:Part)
WITH e, count(p) AS partCount        // 1단계 결과를 다음으로
WHERE partCount >= 5                 // WHERE 는 WITH 뒤에서만 사용 가능
RETURN e.name, partCount
ORDER BY partCount DESC;

함정: WHERE partCount >= 5WITH 없이 쓰면 에러. count() 같은 집계 후 필터는 항상 WITH 가 필요하다.

4-2. MERGE ON CREATE / ON MATCH — 업서트 분기

MERGE 단독은 "있으면 매칭, 없으면 생성". 두 경우에 다른 동작이 필요할 때:

MERGE (e:Equipment {serial: "CNC-001"})
ON CREATE SET e.installedAt = datetime(), e.status = "신규"
ON MATCH  SET e.lastSeenAt = datetime(), e.checkCount = coalesce(e.checkCount, 0) + 1
RETURN e;

함정: MERGE (e:Equipment {serial: "...", status: "운전중"}) 처럼 속성을 여러 개 묶으면 그 조합 모두 일치할 때만 매칭. status 가 다르면 새 노드를 만든다 — 의도와 어긋나는 가장 흔한 실수.

4-3. 인덱스 + UNIQUE 제약 — 성능과 무결성 동시에

100만 노드부터는 인덱스 없으면 단순 lookup 도 초 단위가 든다.

// 자주 찾는 속성은 인덱스
CREATE INDEX equipment_serial IF NOT EXISTS FOR (e:Equipment) ON (e.serial);

// 중복 절대 금지 속성은 UNIQUE 제약 (인덱스 자동 생성)
CREATE CONSTRAINT equipment_serial_unique IF NOT EXISTS
  FOR (e:Equipment) REQUIRE e.serial IS UNIQUE;

// 현재 인덱스 확인
SHOW INDEXES;

운영 룰: KG 적재 전에 인덱스/제약을 먼저 만든다. 적재 후 인덱스 생성은 비싸다. MERGE 가 빠르려면 매칭 속성에 반드시 인덱스 또는 UNIQUE 가 있어야 한다.

4-4. APOC — Neo4j 표준 확장 라이브러리

실무 운영의 절반은 APOC 로 해결된다. Neo4j 설치 시 같이 깔거나 apoc.conf 로 활성.

// 대용량 적재: 1만 건 단위로 배치 (트랜잭션 분할 → 메모리 안전)
CALL apoc.periodic.iterate(
  "MATCH (p:Part) WHERE p.imported IS NULL RETURN p",
  "SET p.imported = true",
  {batchSize: 10000, parallel: false}
);

// 전체 그래프 JSON export (백업/디버깅)
CALL apoc.export.json.all('graph.json', {});

// 동적 라벨 추가
CALL apoc.create.addLabels(node, ['Critical', 'HighPriority']);

추천 출발점 3개: apoc.periodic.iterate (대용량 처리), apoc.export.json.all (백업), apoc.load.json (외부 JSON 로드). 이 셋만 알아도 운영 부담이 절반으로 준다.


다음 task 와의 연결

다음 reading "그래프 데이터 모델링 패턴: 좋은 KG vs 나쁜 KG" 에서 위 4가지가 "좋은 KG" 의 필수 조건으로 다시 등장한다. 특히 인덱스 누락과 MERGE 오용은 "나쁜 KG" 의 가장 흔한 패턴이다.

AI로 학습하기 — 꿀팁
🧪가변 길이 경로로 영향도 분석AI 학습 팁

제조 설비 고장의 파급 영향을 가변 길이 경로 탐색으로 분석하는 실전 Cypher를 AI에게 요청하세요.

Neo4j에서 제조 공정 KG(Equipment→FEEDS_INTO→Process→USES→Component 구조)를 대상으로, 특정 부품 결함이 몇 단계 공정까지 영향을 미치는지 확인하는 가변 길이 경로 쿼리(*1..5)를 작성해줘. shortestPath와 allShortestPaths 결과 차이도 같이 보여줘.
이 팁이 도움이 됐나요?
핵심 포인트
  • 가변 길이 경로(*1..N)로 다단계 원인 추적 가능
  • 영향도 분석: 설비 정지 시 후속 공정까지 파악
  • 집계 함수로 부품 수명 위험도, 인력 가용성 분석
  • 실전 쿼리: 알람 대응, 교체 예정, 인력 공백 분석