본문 바로가기

JanusGraph/JanusGraph 이론

JanusGraph 기초 개념 ③ / Gremlin 쿼리 언어로 그래프 탐색(+Cyper와 비교)

✅ Gremlin이란?

Gremlin은 Apache TinkerPop에서 제공하는 그래프 순회 기반 쿼리 언어입니다.
관계형 DB의 SQL처럼, 그래프 DB에서도 데이터를 찾고, 이어가고, 분석할 수 있도록 도와주는 언어입니다.

🧠 핵심 철학: “그래프를 따라 걸으며 생각하는 방식”
→ 정점부터 출발해서 간선을 따라 이동하며 데이터를 탐색


✅ 기본 구문 구조

Gremlin의 쿼리는 보통 다음과 같이 시작합니다:

g.V() → 정점(Vertex)을 의미
g.E() → 간선(Edge)을 의미

 

여기에 .has(), .out(), .in() 등을 붙여 탐색 방향과 조건을 지정합니다.

 

기본 쿼리 리스트

 

모든 정점 가져오기 V() (예: g.V())
모든 간선 가져오기 E() (예: g.E())
개수 세기 count()
조건 필터링 has() – SQL의 WHERE절과 유사
결과 제한 limit()
범위 필터링 range()
다중 값 조건 포함 within() (예: WHERE x IN (...))
다중 값 조건 제외 without() (예: WHERE x NOT IN (...))

 


✅ 1. TinkerFactory.createModern(), traversal() / 초기화

graph = TinkerFactory.createModern()
 

설명:

  • Gremlin이 기본 제공하는 모던 샘플 그래프를 메모리에 생성합니다.
  • 이 그래프는 다음을 포함합니다:
    • 정점(vertices): 6개 (marko, vadas, lop, josh, ripple, peter)
    • 간선(edges): 6개 (knows, created 등)

📦 출력 예시:

==>tinkergraph[vertices:6 edges:6]

→ 현재 메모리에 로드된 그래프의 구조 요약입니다.


g = graph.traversal()
 

설명:

  • 이 줄은 생성된 그래프에 대해 Gremlin 쿼리를 실행할 준비를 하는 부분입니다.
  • g는 이후 쿼리에서 사용하는 트래버설 시작 객체가 됩니다.

📦 출력 예시:

==>graphtraversalsource[tinkergraph[vertices:6 edges:6], standard]

→ 트래버설 소스가 정상적으로 생성됐다는 뜻입니다.


✅ 2. V() – 모든 정점 가져오기

g.V()

 

  • 결과: v[1] ~ v[6]까지 총 6개의 정점 출력
  • 각 정점의 ID를 나열함

 

Index 정점 ID 이름
0 v[1] marko
1 v[2] vadas
2 v[3] lop
3 v[4] josh
4 v[5] ripple
5 v[6] peter

✅ 3. E() – 모든 간선 가져오기

g.E()

 

  • 간선 ID와 연결된 노드 및 라벨이 함께 출력됨
  • 예: e[7][1-knows->2] = 정점 1에서 2로 향하는 knows 간선

✅ 4. count() – 정점/간선 개수 세기

 

g.V().count()  → 6
g.E().count()  → 6

 

  • 총 정점 수: 6
  • 총 간선 수: 6

✅ 5. has() + valueMap() – 조건 필터 + 속성 보기

 

g.V().has('name', 'josh')               → v[4]
g.V().has('name', 'josh').valueMap()   → [name:[josh], age:[32]]
  • name이 'josh'인 정점을 찾고, 그 값들을 출력

✅ 6. limit() – 결과 수 제한

g.V().limit(1)                         → v[1]
g.V().limit(1).valueMap()             → [name:[marko], age:[29]]
  • 정점 1개만 가져오고, 해당 속성 출력

✅ 7. range() – 범위 지정

g.V().range(2, 4).valueMap()
  • 정점 인덱스 2~3번을 가져옴 (4는 포함 X)
  • v[3], v[4] → [lop], [josh] 출력
Index 정점 ID 이름
0 v[1] marko
1 v[2] vadas
2 v[3] lop
3 v[4] josh
4 v[5] ripple
5 v[6] peter

 


✅ 8. within() – 다중 포함 필터

g.V().has('name', within('josh', 'marko')).valueMap()

 

  • 이름이 josh 또는 marko인 정점만 필터링
  • 결과: [marko, josh]

✅ 9. without() – 다중 제외 필터

g.V().has('name', without('josh', 'marko')).valueMap()

 

  • josh, marko를 제외한 모든 정점 출력
  • 결과: vadas, lop, ripple, peter

 


✅ 예제 ①: 정점 찾기

g.V().has('name', 'Marko')

→ 이름이 Marko인 정점을 찾는다


✅ 예제 ②: 특정 정점이 연결된 다른 정점 찾기

g.V().has('name', 'Marko').out('knows')

→ Marko가 알고 있는(knows) 사람들을 찾는다

📌 여기서 out('knows')는 ‘knows’ 라벨을 가진 간선을 따라 나가는 정점들을 의미합니다.


✅ 예제 ③: 정점 간 연결된 간선 정보까지 확인

g.V().has('name', 'Marko').outE('knows')

→ Marko와 다른 정점을 연결하는 "knows" 간선을 직접 가져옴
(간선의 속성을 보고 싶을 때 사용)


✅ 예제 ④: 간선 속성 조회

g.V().has('name', 'Marko') .outE('knows') .values('since')

→ Marko가 다른 사람을 알게 된 시점(since 속성)을 출력


✅ 예제 ⑤: 양방향 탐색

g.V().has('name', 'Josh').both('knows')

→ Josh와 'knows' 관계로 연결된 정점 모두 조회
(→ 들어오거나 나가는 방향 모두 포함)


✅ Gremlin 쿼리 예시 구조 요약

표현 의미
g.V() 전체 정점 조회
g.E() 전체 간선 조회
.has(key, value) 속성 조건 필터
.out('label') 특정 라벨의 간선을 따라 나가는 방향으로 정점 탐색
.in('label') 특정 라벨의 간선을 따라 들어오는 방향으로 정점 탐색
.both('label') 양방향 탐색
.outE() / .inE() 간선 자체를 가져옴
.values('property') 속성값 출력

✅ 왜 중요한가?

Gremlin은 JanusGraph의 기본 쿼리 언어이기 때문에, 다음과 같은 상황에 핵심적입니다:

  • 복잡한 네트워크 관계 분석
  • 특정 조건의 사용자 그룹 탐색
  • 다단계 관계 조회 (e.g., 친구의 친구 찾기)
  • 시간 기반 속성 탐색

⚔️ Gremlin vs Cypher: 그래프 쿼리 언어 비교

항목 Gremlin (JanusGraph) Cypher (Neo4j)
기반 구조 순회 기반 (Traversal) 선언형 (Declarative)
쿼리 스타일 데이터를 따라 이동 (Imperative) 결과를 선언하고 추론 (SQL-like)
표현 방식 .out(), .in(), .has() 등 체이닝 방식 MATCH, RETURN, WHERE 같은 SQL-like 키워드
러닝 커브 초반에 약간 난해, 프로그래밍적 사고 필요 SQL에 익숙한 사람에게는 익숙하고 직관적
유연성 체이닝이 가능해 다단계 복잡 쿼리에 강함 읽기 쉬운 문법, 단순 탐색과 패턴 매칭에 탁월
기본 지원 DB JanusGraph, Apache TinkerPop 호환 DB Neo4j 전용 (→ 다른 DB와 호환성 낮음)
함수 스타일 JavaScript, Python 등과 유사한 체이닝 함수 SQL과 유사한 문법 구성

🔍 예제 비교: Marko가 아는 사람을 찾는 쿼리

Gremlin (JanusGraph)

g.V().has('name', 'Marko').out('knows')

Cypher (Neo4j)

MATCH (a:Person {name: 'Marko'})-[:KNOWS]->(b)
RETURN b
  • Gremlin은 “Marko → out(‘knows’) → 누구?” 식의 탐색 중심
  • Cypher는 “이런 패턴을 가진 노드를 찾아라”라는 패턴 매칭 중심

🔍 예제 비교 2: 특정 이름을 가진 정점 조회

| 목적: 이름이 Marko인 정점 검색 |

 

Gremlin

g.V().has('name', 'Marko')

 

Cypher

MATCH (n {name: 'Marko'})
RETURN n

 

→ Cypher는 더 직관적이고 선언적, Gremlin은 더 코딩스럽고 유연한 순회 기반


🔍 예제 비교 3: 정점 + 간선 + 속성 출력

| 목적: Marko가 아는 사람들과 관계 정보도 함께 출력 |

 

Gremlin

g.V().has('name', 'Marko').outE('knows').as('e').inV().as('v') .select('e', 'v')

 

 

Cypher

MATCH (a:Person {name: 'Marko'})-[r:KNOWS]->(b)
RETURN r, b

 

→ 복잡한 그래프 traversal에서 Gremlin은 노드 ↔ 간선 연결을 자유롭게 지정 가능
→ Cypher는 명확하고 읽기 쉬운 형태로 노드와 간선을 동시에 다룰 수 있음


✅ 결론 요약

관점 Gremlin (JanusGraph) Cypher (Neo4j)
프로그래밍적 유연성 ✅ 매우 높음 (임의 루프, 조건 순회 등 가능) ❌ 제한적 (일반적인 패턴 탐색에 적합)
학습 난이도 📈 조금 높음 (함수 체이닝 방식) 📉 쉬움 (SQL 유사 문법)
확장성 ✅ Apache TinkerPop 기반 다양한 DB에서 사용 가능 ❌ 사실상 Neo4j 전용
실용성 복잡한 그래프 로직 구현에 유리 단순한 탐색/패턴 매칭에 빠르고 쉬움

🧩 추천 활용 방식

  • 🔎 초보자, 직관적 쿼리가 필요한 환경Cypher
  • 🧠 복잡한 탐색/분기/동적 관계 추적Gremlin