본문 바로가기

문돌이 존버/DB 및 SQL 스터디

Neo4j 그래프 DB 개념 및 cyper 쿼리 예제

반응형

Neo4j 그래프 형태에 대해 아래 그림을 예시로 들어 간략히 설명하겠습니다. 먼저 Neo4j 기본 구성 요소는 크게 4가지가 있습니다.

1. 노드(Nodes)
2. 관계(Relationships)
3. 속성(Properties)
4. 라벨(Labels)

<출처: Neo4j 홈페이지>

위 그림에서 초록색, 분홍색, 파란색 원들이 노드입니다. 각 노드에 직원, 회사, 도시가 적혀져 있는데 이를 라벨이라고 합니다. 노드 간 화살표는 관계를 나타내며 타입(:LOCATED_IN)과 방향(-> or <-)으로 관계를 정의합니다. 한 노드에 있는 name-value 쌍(name: Amy Peters)이 속성을 가리킵니다. 

아래는 Neo4j의 cypher 쿼리문으로 데이터를 생성하고 매칭하는 예시입니다. 

// CREATE Graph Patterns
CREATE(:Person{name:"Dan"})-[:LOVES]->(:Person{name:"Ann"})

// MATCH Graph Patterns
MATCH(:Person{name:"Dan"})-[:LOVES]->(whom) RETURN whom

아래는 cypher에서 데이터를 표시할 때 사용하는 기본 구성 요소입니다. 

Nodes: () 
Relationships: -[:DIRTECTED]->
Pattern: ()-[]-()
         : ()-[]->()
         : ()<-[]-()
MATCH (m: Movie) RETURN m​

MATCH (p:Person)-[r:ACTED_IN]->(m:Movie)
RETURN p, r, m LIMIT 25

// Or 
MATCH path = (:Person)-[:ACTED_IN]-> (:Movie) // path라는 변수 선언
RETURN path limit 25

테이블 형식으로 데이터를 보기 위한 쿼리문입니다. 

속성은 {변수}.{속성_키}로 접근할 수 있다
MATCH (m:Movie)
RETURN m.title, m.released

WHERE, AND 절을 추가한 예시입니다. 

MATCH (p:Person)-[:ACTED_IN]->(m:Movie),
      (d:Person)-[:DIRECTED]->(m:Movie)
WHERE p.name = 'Tom Hanks' AND p.born = 1956
AND d.name = 'Robert Zemeckis' AND d.born = 1951
RETURN m.title, m.released

아래는 SQL의 aggregate 함수를 사용하는 것인데요. Neo4j는 따로 groupby를 하지 않기 때문에 아래처럼 count(*)만 선언해주면 출연한 영화 횟수를 배우에 따라 자동으로 카운팅합니다.

주의할 것은 p.name이 존재하지 않는 경우로, 이때는 DB에 있는 전체 영화 갯수를 리턴합니다. 

MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
RETURN p.name, count(*) AS numberOfMovies

MATCH (n) DETACH DELETE n; // DETACH는 노드, 관계가 엮인 데이터의 경우 사용
MATCH (n) DELETE n; // 관계가 없는 경우

새로운 노드를 생성하고 속성을 부여해보겠습니다. 

CREATE (m: Movie {title: 'Mystic River', released:2003})
RETURN m

새로 만든 노드에 태그라인이란 속성을 추가로 부여합니다. 이때 사용하는 기능은 SET입니다.

MATCH (m:Movie {title: 'Mystic River'})
SET m.tagline = 'We bury our sins here, Dave. We was them clean.'
RETURN m

이제 기존에 있던 사람 데이터를 가지고 위에서 만든 영화 노드와 새로운 관계를 생성해보겠습니다. Kevin Bacon이 Mystic River에 Sean 역할로 출연했다는 정보입니다. 

관계를 설정하고 싶은 노드를 MATCH로 불러오고, CREATE로 새로운 관계를 설정해주면 됩니다. 

MATCH (m:Movie {title: 'Mystic River'})
MATCH (p:Person {name: 'Kevin Bacon'}) // 이미 DB에 있는 사람 데이터
CREATE (p)-[r:ACTED_IN {roles: ['Sean']}]->(m) // 새로운 role 생성
RETURN p, r, m

추가로 살펴볼 기능은 MERGE입니다. MERGE는 2가지 기능을 합니다.

1. Match: 입력한 노드의 속성이 이미 존재할 경우 단순 매치
2. Create: 입력한 노드 속성 중 하나라도 존재하지 않는 경우 새롭게 생성
MERGE (p:Person {name: 'Kevin Bacon'}) // <- just match
RETURN p

MERGE (p:Person {name: 'Kevin Bacon', oscar: true}) // <- create property "oscar: true"
RETURN p

위 코드에서 Kevin Bacon이라는 노드는 원래 oscar: true 라는 속성이 없었습니다. 따라서 처음 MERGE 결과에선 해당 노드와 매치시켜 리턴을 해준 반면, 두 번째 MERGE 결과에선 oscar: true 속성이 추가로 입력되었기 때문에 이를 Kevin Bacon 노드에 추가시키고 동시에 리턴합니다. 

이외에도 유니크 제약(Unique Constraints)을 부여하는 방법이 있습니다. 먼저 아래처럼 Label이란 노드의 속성 property를 유일무이한 것으로 만들어줍니다.

CREATE CONSTRAINT ON (label: Label)
ASSERT label.property IS UNIQUE

이후 Label 노드를 생성하고 property를 snack으로 해봅시다. 

CREATE (label: Label {property: "snack"})
RETURN label

다시 한 번 property가 snack인 Label 노드를 생성하려고 하면 아래와 같은 에러 메세지가 등장합니다. 이미 property=snack이란 노드가 존재한다는 것이죠. 

이번엔 반드시 입력해야 하는 속성을 설정할 수 있습니다. 즉 어떤 데이터를 생성할 때 반드시 해당 속성을 포함해야 한다는 제약을 걸어두는 것이죠. 아래에선 Test 노드를 만들 때 반드시 name이란 속성을 설정하도록 했습니다. 

CREATE CONSTRAINT ON (test: Test)
ASSERT EXISTS (test.name)
MATCH (test: Test {name: "English"})
RETURN test

한 번 name이란 속성 대신 새로운 속성 age를 선언해보면 제약 조건을 위반하여 에러 메세지가 뜨는 것을 볼 수 있습니다. 

CREATE (test:Test {age: "20"})
RETURN test

관계에도 제약 조건을 걸 수 있습니다. 아래 예시를 보면 데이터를 생성할 때에는 반드시 ()-[]->()의 형식을 가지는 관계를 설정해야 함을 알 수 있죠. 

CREATE CONSTRAINT ON ()-[rel:REL_TYPE]->()
ASSERT EXISTS(rel.name)

마지막으로 빠르게 데이터를 살펴볼 수 있는 기능인 STARTS WITH 입니다. 파이썬의 startswith 기능과 비슷한데, "Tom"으로 시작하는 사람 이름을 다 찾으라는 것입니다. 

CONTAINS: 특정 문자를 포함하는지 확인
ENDS WITH: 어떤 문자로 끝이 나는지 확인
MATCH (n:Person)
WHERE n.name STARTS WITH "Tom"
RETURN n.name

728x90
반응형