그래프 쓰기#

쓰기 절에는 이름을 붙일 만한 자체 패턴 일치 기능이 없습니다. 앞에서 온 것이 무엇이든 그 행들을 받아 각 행에 대해 무언가를 합니다. 그래서 맨 처음의 CREATE를 빼면 모든 쓰기가 같은 모양을 따릅니다. 먼저 찾고, 그다음 씁니다.

이 페이지의 계획이 그것뿐입니다. 필요한 순서대로 쓰기 절을 훑습니다. CREATE, MERGE, SET, REMOVE, DELETE입니다. 그리고 트랜잭션이 그래프에 무엇을 하는지로 마칩니다. 각각의 정식 구문은 레퍼런스에 있습니다. CREATE, MERGE, SET, REMOVE, DELETE입니다.

예제는 예제 데이터셋에서 실행되며 실행 뒤에는 처음 상태로 되돌려 놓습니다. 그래서 페이지를 따라 내려가며 실행해도 다른 페이지가 출력하는 개수와 계속 맞습니다.

새로 만들기#

기존의 무엇도 바인딩하지 않는 패턴을 준 CREATE는 정점을 만듭니다:

CREATE (p:person {name: 'Gloria Foster'}) RETURN p;
                   p                    
----------------------------------------
 person[4.153]{"name": "Gloria Foster"}

RETURN은 생략할 수 있지만 레이블은 그렇지 않습니다. 레이블 없이 만든 정점은 ag_vertex에 놓이고, 간선에는 항상 레이블이 있어야 합니다. 결과에 나온 id는 그 레이블의 시퀀스가 어디까지 갔는지에 따른 값이므로, 직접 실행하면 다른 값이 나옵니다.

이미 존재하는 두 대상을 연결하려면 먼저 그것들을 찾고 그다음 간선을 만듭니다. 일치시키지 않은 정점에 간선을 쓰는 방법은 없습니다:

MATCH (p:person {name: 'Gloria Foster'}), (m:movie {title: 'The Matrix'})
CREATE (p)-[r:acted_in {role: 'The Oracle'}]->(m)
RETURN r;
                        r                        
-------------------------------------------------
 acted_in[5.71][4.153,3.1]{"role": "The Oracle"}

간선은 label[id][start,end]{properties} 형태로 출력됩니다. 여기서 (p)와 (m)에는 레이블도 속성 맵도 없습니다. 이미 바인딩된 변수를 가리키는 참조이며, 여기에 레이블을 붙이면 그 대신 새 정점을 만들게 됩니다.

요소가 하나보다 긴 CREATE 패턴은 그 안의 모든 것을 만듭니다:

CREATE (d:person {name: 'Marc Caro'})-[:directed]->(f:movie {title: 'The City of Lost Children', year: 1995})
RETURN d.name AS director, f.title AS film;
  director   |            film             
-------------+-----------------------------
 "Marc Caro" | "The City of Lost Children"

INSERT는 같은 절의 ISO/IEC 39075 표기이며 같은 패턴을 받습니다: INSERT (:person {name: 'Ada Lovelace'}). INSERT를 참고하십시오.

없을 때만 만들기#

MERGE는 자기 패턴을 일치시키고, 일치하는 것이 없을 때만 그것을 만듭니다:

MERGE (p:person {name: 'Gloria Foster'}) RETURN p;
                   p                    
----------------------------------------
 person[4.153]{"name": "Gloria Foster"}

이것은 두 번째 정점을 만든 것이 아니라 앞 절에서 만든 정점을 찾은 것입니다. 무엇을 기준으로 일치시키는지 주의하십시오. 맵에 있는 모든 속성을 포함한 패턴 전체입니다. 속성을 하나 더하면 그 패턴은 이미 가진 정점을 설명하지 않게 되므로, MERGE는 그 패턴이 설명하는 정점을 새로 만듭니다:

MERGE (p:person {name: 'Gloria Foster', born: 1933}) RETURN p;
                          p                           
------------------------------------------------------
 person[4.155]{"born": 1933, "name": "Gloria Foster"}

이제 둘이 됐습니다:

MATCH (p:person {name: 'Gloria Foster'}) RETURN id(p) AS id, p.born AS born ORDER BY id;
  id   | born 
-------+------
 4.153 | 
 4.155 | 1933

그러므로 어떤 대상에 대해 아는 모든 것을 맵에 담아 MERGE하는 것은 중복 생성기입니다. 대상을 식별하는 키만으로 MERGE하고 나머지 값은 그 뒤에 적용하는 것이 멱등합니다. ON CREATE SET과 ON MATCH SET이 그것을 위해 있습니다:

MERGE (p:person {name: 'Joe Pantoliano'})
  ON CREATE SET p.born = 1951, p.seen = 1
  ON MATCH SET p.seen = p.seen + 1
RETURN p;
                                p                                 
------------------------------------------------------------------
 person[4.156]{"born": 1951, "name": "Joe Pantoliano", "seen": 1}

다시 실행하면 ON MATCH 분기만 발동합니다:

                                p                                 
------------------------------------------------------------------
 person[4.156]{"born": 1951, "name": "Joe Pantoliano", "seen": 2}

MERGE는 간선에도 같은 방식으로 동작하며, 간선 패턴을 일치시킨다는 것은 그 레이블, 방향, 속성 맵, 그리고 양 끝점까지 일치시킨다는 뜻입니다. 끝점은 먼저 MATCH로 바인딩해서 MERGE가 간선만 판단하게 하십시오. 아래 간선은 이미 있으므로 아무것도 만들어지지 않습니다:

MATCH (p:person {name: 'Christopher Nolan'}), (m:movie {title: 'Inception'})
MERGE (p)-[r:directed]->(m) RETURN r;
            r             
--------------------------
 directed[6.8][4.8,3.5]{}

계속하기 전에 Gloria Foster를 하나로 되돌립니다:

MATCH (p:person {name: 'Gloria Foster', born: 1933}) DELETE p;

식별 속성에 걸어 둔 고유성 제약이 이 규율의 오래가는 형태입니다. 올바른 MERGE를 쓰는 데 의존하지 않고 중복 자체를 거부하기 때문입니다. 인덱스와 제약을 참고하십시오.

속성 바꾸기#

속성 하나에 대한 SET은 그 속성을 추가하거나 교체합니다:

MATCH (p:person {name: 'Gloria Foster'}) SET p.born = 1933 RETURN p;
                          p                           
------------------------------------------------------
 person[4.153]{"born": 1933, "name": "Gloria Foster"}

SET p += { ... }는 맵을 병합해 넣으며, 키를 추가하고 겹치는 키는 덮어씁니다:

MATCH (p:person {name: 'Gloria Foster'}) SET p += {died: 2001, city: 'Chicago'} RETURN p;
                                           p                                           
---------------------------------------------------------------------------------------
 person[4.153]{"born": 1933, "city": "Chicago", "died": 2001, "name": "Gloria Foster"}

SET p = { ... }는 속성 맵 전체를 교체합니다. 새 맵에 없는 것은 모두 사라집니다:

MATCH (p:person {name: 'Gloria Foster'}) SET p = {name: 'Gloria Foster', born: 1933} RETURN p;
                          p                           
------------------------------------------------------
 person[4.153]{"born": 1933, "name": "Gloria Foster"}

+=와 =의 차이는 UPDATE ... SET one_column과 행 전체를 교체하는 것의 차이이며, 잘못 입력하기 쉽습니다.

null을 할당하는 것은 null을 저장하는 것이 아닙니다. 그 속성을 지웁니다:

MATCH (p:person {name: 'Gloria Foster'}) SET p.born = NULL RETURN p;
                   p                    
----------------------------------------
 person[4.153]{"name": "Gloria Foster"}

그러므로 null로 설정한 속성과 애초에 없던 속성은 같은 상태이고, 그 뒤에 둘을 구별할 방법이 없습니다. allow_null_properties가 이 동작을 바꿔 null을 저장하며, 기본적으로 꺼져 있습니다. REMOVE는 같은 일을 명시적으로 표기한 것입니다:

MATCH (p:person {name: 'Joe Pantoliano'}) REMOVE p.seen RETURN p;
                           p                           
-------------------------------------------------------
 person[4.156]{"born": 1951, "name": "Joe Pantoliano"}

간선의 속성은 정점의 속성과 똑같이 씁니다:

MATCH (:person {name: 'Gloria Foster'})-[r:acted_in]->(:movie {title: 'The Matrix'})
SET r.credited = true RETURN r;
                                 r                                 
-------------------------------------------------------------------
 acted_in[5.71][4.153,3.1]{"role": "The Oracle", "credited": true}

참고

승격된 속성은 “속성은 무엇이든 담을 수 있다”의 유일한 예외입니다. 어떤 키 뒤에 타입 있는 컬럼이 놓이면, JSON 타입이 그 컬럼에 맞지 않는 쓰기는 중단됩니다. 속성을 컬럼으로 승격하기를 참고하십시오.

지우기#

DELETE는 일치시킨 것을 지우고 그것을 돌려주므로, 무엇이 사라졌는지 볼 수 있습니다:

MATCH (p:person {name: 'Joe Pantoliano'}) DELETE p RETURN p;
                           p                           
-------------------------------------------------------
 person[4.156]{"born": 1951, "name": "Joe Pantoliano"}

아직 간선이 남아 있는 정점은 지울 수 없습니다. 끝점이 없는 간선은 존재할 수 없기 때문입니다:

MATCH (p:person {name: 'Gloria Foster'}) DELETE p;
ERROR:  Vertex with connected edges cannot be removed
HINT:  Use DETACH DELETE or remove connected edges before deleting the vertex.

간선을 직접 지우거나:

MATCH (:person {name: 'Gloria Foster'})-[r:acted_in]->() DELETE r RETURN r;
                                 r                                 
-------------------------------------------------------------------
 acted_in[5.71][4.153,3.1]{"role": "The Oracle", "credited": true}

DETACH DELETE를 쓰십시오. 이것은 정점을 그에 닿아 있는 모든 간선과 함께 지웁니다:

MATCH (d:person {name: 'Marc Caro'}) DETACH DELETE d;
MATCH (f:movie {title: 'The City of Lost Children'}) DETACH DELETE f;
MATCH (p:person {name: 'Gloria Foster'}) DELETE p;

데이터셋이 원래대로 돌아왔습니다:

MATCH (n) RETURN count(n) AS elements;
 elements 
----------
 46

이 거부는 불편함이 아니라 안전장치입니다. 허브 정점에 DETACH DELETE를 쓰면 의도한 것보다 훨씬 많은 것이 지워질 수 있으므로, 무엇이든 지우기 전에 잃으려는 간선을 일치시켜 세어 보십시오.

방금 쓴 것을 읽기#

쓰기 절이 문장의 끝은 아닙니다. 그 출력 행은 파이프라인을 따라 계속 내려가고, 그 뒤의 읽기는 쓰기가 남겨 놓은 상태의 그래프를 봅니다:

CREATE (:person {name: 'Gloria Foster'})
WITH 1 AS done
MATCH (p:person {name: 'Gloria Foster'})
RETURN count(p) AS found;
 found 
-------
 1

플래너는 다음 읽기가 실행되기 전에 쓰기의 효과를 구체화해서 그것을 보장합니다. 이것이 “eager” 계획이며, EXPLAIN도 그렇게 표시합니다. 그 비용과 그것을 금지하는 설정은 쿼리 튜닝이 다룹니다.

트랜잭션#

그래프는 데이터베이스 안의 테이블이므로, 그래프에 쓰는 것은 평범한 트랜잭션 쓰기입니다. 켜야 할 것도 없고 두 번째 커밋을 생각할 필요도 없습니다. 한 트랜잭션 안의 Cypher 쓰기와 SQL 쓰기는 함께 커밋되거나 함께 롤백됩니다.

CREATE TABLE import_log (what text, at timestamptz DEFAULT now());
BEGIN;
CREATE (:person {name: 'Gloria Foster'});
INSERT INTO import_log (what) VALUES ('added Gloria Foster');
MATCH (p:person {name: 'Gloria Foster'}) RETURN count(p) AS found;
 found 
-------
 1

트랜잭션 안에서는 두 쓰기가 모두 있습니다. 롤백하면 둘 다 없습니다:

ROLLBACK;
MATCH (p:person {name: 'Gloria Foster'}) RETURN count(p) AS found;
 found 
-------
 0
SELECT count(*) AS logged FROM import_log;
 logged 
--------
      0
DROP TABLE import_log;

PostgreSQL이라는 점에서 따라오는 나머지 모든 것이 여기에도 따라옵니다. 격리 수준, 세이브포인트, 레이블 테이블에 대한 SELECT ... FOR UPDATE, 같은 정점에 쓰는 두 세션 사이의 교착 상태 탐지가 그렇습니다. 원한다면 대량 적재를 한 트랜잭션으로 할 수 있고, 트랜잭션 안의 CREATE GRAPH는 다른 DDL과 마찬가지로 롤백됩니다.

반대 방향으로는 관문이 둘 있습니다. SQL INSERT, UPDATE, DELETE, MERGE로 레이블에 행을 쓰는 것은 enable_graph_dml이 켜져 있지 않으면 거부됩니다. ALTER TABLE로 레이블의 모양을 바꾸는 것은 enable_graph_ddl이 켜져 있지 않으면 거부됩니다. 어느 쪽이든 그래프 카탈로그가 더 이상 사실이 아닌 것을 설명하는 상태로 만들 수 있습니다. 그래프가 어떻게 저장되는가를 참고하십시오.

다음에 갈 곳#

  • 데이터 적재 — 기존 테이블에 대한 LOAD FROM을 포함한 대량 적재입니다.

  • 쿼리 조합하기 — WITH, UNWIND, 집계입니다. 모두 쓰기 절 앞에서 유용합니다.

  • 보안 — 레이블에 대한 권한 부여와 행 수준 보안입니다.