{#types-data-types} # 데이터 타입 AgensGraph는 PostgreSQL의 타입 집합에 다섯 개를 더합니다. `graphid`, `vertex`, `edge`, `graphpath`, `rowid`입니다. 그래프가 저장하는 그 밖의 모든 것은 레이블의 `properties` 컬럼에 담긴 `jsonb`입니다. PostgreSQL의 모든 타입은 여전히 쓸 수 있고 PostgreSQL이 문서화한 대로 동작합니다. 이 다섯은 고정된 OID로 `pg_type`에 정의돼 있으므로 모든 데이터베이스에서 같고 확장이 필요하지 않습니다. | 타입 | 저장 방식 | 담는 것 | | --- | --- | --- | | `graphid` | 8바이트, 값으로 | 정점 하나 또는 간선 하나의 식별자입니다. | | `vertex` | 복합, 가변 | id, 속성 맵, 그리고 그 행의 물리적 위치입니다. | | `edge` | 복합, 가변 | id, 끝점 id 둘, 속성 맵, 그리고 그 행의 위치입니다. | | `graphpath` | 복합, 가변 | 정점의 배열과 간선의 배열입니다. | | `rowid` | 10바이트, 참조로 | 테이블 OID와 행 포인터입니다. 내부용입니다. | {#types-graphid} ## graphid `graphid`는 정점이나 간선의 식별자입니다. 간선은 자기 끝점을 지목하려고 그것을 저장합니다. 두 부분으로 된 64비트 값입니다. 상위 16비트가 **레이블 id**, 하위 48비트가 레이블마다 매겨지는 시퀀스인 **로컬 id**입니다. 출력 형태는 *labid*`.`*locid*입니다. 그 배치가 왜 플래너에게 중요한지는 [그래프가 어떻게 저장되는가](#graph-on-postgresql-identity-the-graphid)가 설명합니다. 이 절은 그 정식 세부 내용입니다. 두 함수가 그 부분들을 다시 분해합니다: ```sql SELECT id, graphid_labid(id) AS labid, graphid_locid(id) AS locid FROM moviekb.person WHERE properties->>'name' = 'Keanu Reeves'; ``` ``` id | labid | locid -----+-------+------- 4.1 | 4 | 1 ``` `graphid(`*labid*`, `*locid*`)`가 그 부분들로 하나를 만듭니다. 레이블의 `id` 컬럼이 기본값 표현식에서 호출하는 것과 같은 함수입니다. {#types-range} ### 범위 레이블 id는 16비트, 로컬 id는 48비트이므로 가장 큰 `graphid`는 이렇습니다: ```sql SELECT graphid(65535, 281474976710655) AS highest; ``` ``` highest ----------------------- 65535.281474976710655 ``` 어느 부분이든 범위를 벗어나면 잘리는 것이 아니라 거부됩니다: ```sql SELECT graphid(65536, 1); ``` ``` ERROR: labid out of range: 65536 ``` ```sql SELECT graphid(3, 281474976710656); ``` ``` ERROR: locid out of range: 281474976710656 ``` 0은 *레이블 없음*으로 예약돼 있으므로, 데이터베이스는 최대 65,535개의 레이블을, 레이블은 최대 248 − 1개의 요소를 가집니다. 이 예약은 검사가 아니라 관례이므로 `graphid(0, 1)`은 `0.1`을 만듭니다. 다만 레이블 id 0을 배정하는 것은 아무것도 없습니다. 이런 수치는 PostgreSQL 자신의 것과 함께 [제한](../appendix/limits.md)이 모아 두었습니다. {#types-casts} ### 형 변환 `graphid`로 들어가는 형 변환은 `numeric`에서 오는 것 하나뿐이고, 그것은 암시적입니다. `integer`에서 오는 것은 없으므로, 소수점 없이 적은 정수는 변환되지 않습니다: ```sql SELECT 1::graphid; ``` ``` ERROR: cannot cast type integer to graphid LINE 1: SELECT 1::graphid; ^ ``` 두 부분을 적으십시오. 소수점이 있는 수치 리터럴은 형 변환되고, *labid*`.`*locid* 형태의 문자열은 파싱됩니다: ```sql SELECT 1.1::graphid AS from_numeric, '3.1'::graphid AS from_text; ``` ``` from_numeric | from_text --------------+----------- 1.1 | 3.1 ``` `numeric` 형 변환은 그 타입이 출력하는 방식으로 숫자를 읽습니다. 그래서 `1.1`은 1.1이라는 수가 아니라 레이블 1의 요소 1입니다. 소수 자리가 없는 numeric에는 로컬 id가 없어서 거부됩니다. `1::numeric::graphid`는 `invalid input syntax for type graphid: "1"`을 냅니다. `graphid`에서 나가는 형 변환은 없습니다. 부분이 필요하면 `graphid_labid()`와 `graphid_locid()`를 쓰거나, 그냥 출력되게 두십시오. {#types-vertex-and-edge} ## vertex와 edge `vertex`는 필드 세 개로 된 복합 값입니다. `id graphid`, `properties jsonb`, 그리고 그 행의 물리적 위치를 담은 `tid`입니다. 쓰기는 그 `tid`로 그 행을 다시 찾습니다. `edge`는 같은 세 필드에 `start graphid`와 `end graphid`가 더해진 것입니다. 레이블과 id, 속성을 보여 주는 형태로 출력됩니다: ```cypher MATCH (p:person {name: 'Keanu Reeves'}) RETURN p; ``` ``` p --------------------------------------------------- person[4.1]{"born": 1964, "name": "Keanu Reeves"} ``` 간선은 자기 id와 속성 사이에 양 끝점을 출력하며, 시작점이 먼저 옵니다: ```cypher MATCH (:person {name: 'Keanu Reeves'})-[r:acted_in]->(:movie {title: 'The Matrix'}) RETURN r; ``` ``` r --------------------------------------- acted_in[5.1][4.1,3.1]{"role": "Neo"} ``` 그래서 모양은 정점이 `label[id]{properties}`, 간선이 `label[id][start,end]{properties}`입니다. 속성이 없는 요소는 빈 맵을 출력합니다. `directed[6.8][4.8,3.5]{}`입니다. `tid` 필드는 두 출력 형태 어디에도 들어 있지 않고, 의존할 만한 것도 아닙니다. 그 행이 갱신되면 값이 바뀝니다. {#types-what-you-can-do-with-them} ### 그것으로 할 수 있는 일 `vertex`와 `edge`에는 각각 비교 연산자 여섯 개가 있고, 그 밖에는 아무것도 없습니다: | 타입 | 연산자 | | --- | --- | | `graphid` | `=` `<>` `<` `<=` `>` `>=` | | `vertex` | `=` `<>` `<` `<=` `>` `>=` | | `edge` | `=` `<>` `<` `<=` `>` `>=` | | `rowid` | `=` `<>` `<` `<=` `>` `>=` | | `graphpath` | 없음 | **비교는 식별자만으로 합니다.** `vertex_eq`와 그 형제 함수들은 `id` 필드를 비교하고 속성은 전혀 보지 않습니다. 속성이 똑같고 id가 다른 두 정점은 같지 않습니다: ```cypher CREATE GRAPH twins; SET graph_path = twins; CREATE VLABEL pair; CREATE (:pair {k: 1}), (:pair {k: 1}); MATCH (a:pair), (b:pair) WHERE id(a) < id(b) RETURN a, b, a = b AS equal, properties(a) = properties(b) AS same_properties; ``` ``` a | b | equal | same_properties -------------------+-------------------+-------+----------------- pair[3.1]{"k": 1} | pair[3.2]{"k": 1} | f | t ``` 순서도 같은 규칙에서 따라옵니다. `a < b`는 `graphid` 값을 비교하므로, 먼저 레이블 순서이고 그다음 레이블 안에서는 삽입 순서입니다. 이 순서는 전순서이고 안정적이어서 `ORDER BY`의 동률 해소에 좋습니다. 데이터를 뜻 있게 정렬해 주는 것은 아닙니다. ```cypher MATCH (a:person {name: 'Keanu Reeves'}), (b:person {name: 'Carrie-Anne Moss'}) RETURN a = b AS equal, a < b AS less, id(a) AS id_a, id(b) AS id_b; ``` ``` equal | less | id_a | id_b -------+------+------+------ f | t | 4.1 | 4.3 ``` 대신 속성을 비교하려면 속성을 비교하십시오. `properties(a) = properties(b)`처럼 쓰거나, 뜻하는 키를 이름으로 부르십시오. 두 타입 모두 암시적으로 `jsonb`로 형 변환됩니다. 그 변환은 속성 맵을 남기고 식별자는 버립니다. `pg_cast`의 `vertex_to_jsonb`와 `edge_to_jsonb`입니다. 그래서 `m::jsonb`는 `properties(m)`입니다. 반대 방향의 형 변환은 없습니다. {#types-graphpath} ## graphpath `graphpath`는 배열 두 개로 된 복합 타입입니다. `vertices vertex[]`와 `edges edge[]`입니다. 간선 *n*개인 경로는 정점 *n* + 1개를 담으므로, 정점 하나에 바인딩된 경로는 정점 하나에 간선이 없습니다: ```cypher MATCH p = (a:person {name: 'Keanu Reeves'}) RETURN p; ``` ``` p ----------------------------------------------------- [person[4.1]{"born": 1964, "name": "Keanu Reeves"}] ``` 보행 순서대로 정점·간선·정점이 번갈아 오는 구성원 목록을 대괄호로 감싸고 쉼표로 구분해 출력합니다. 두 배열이 두 개의 배열로 출력되는 것이 아니라 서로 엇갈려 놓입니다. 그래서 간선 하나인 경로는 구성원 세 개를, 간선 *n*개인 경로는 2*n* + 1개를 출력합니다. 구성원에는 `nodes()`와 `edges()`로 닿습니다. 그 두 함수가 두 배열을 돌려줍니다. `length()`는 간선 개수입니다: ```cypher MATCH p = (:person {name: 'Keanu Reeves'})-[:acted_in]->(:movie {title: 'The Matrix'}) RETURN length(p) AS hops, nodes(p)[0] AS first_vertex, edges(p)[0] AS only_edge; ``` ``` hops | first_vertex | only_edge ------+---------------------------------------------------+--------------------------------------- 1 | person[4.1]{"born": 1964, "name": "Keanu Reeves"} | acted_in[5.1][4.1,3.1]{"role": "Neo"} ``` `graphpath`에는 **자기 연산자가 없습니다.** 같음도, 순서도 없습니다. Cypher에서 경로 둘을 비교하는 것은 거부됩니다. 호출할 연산자가 없으니 그 표현식은 `jsonb`로 되돌아가는데, 경로는 `jsonb` 값이 아닙니다: ```cypher MATCH p1 = (:person {name:'Keanu Reeves'})-[:acted_in]->(:movie {title:'The Matrix'}), p2 = (:person {name:'Keanu Reeves'})-[:acted_in]->(:movie {title:'The Matrix'}) RETURN p1 = p2; ``` ``` ERROR: graph object cannot be jsonb LINE 3: RETURN p1 = p2; ^ ``` SQL에서는 비교가 됩니다. 복합 값에 대한 PostgreSQL의 일반 연산자를 통해서이고, 그것은 두 배열을 필드 단위로 훑습니다: ```psql SELECT a = b AS equal, a < b AS less FROM (MATCH p = (:person {name:'Keanu Reeves'})-[:acted_in]->(:movie {title:'The Matrix'}) RETURN p AS a) x, (MATCH p = (:person {name:'Keanu Reeves'})-[:acted_in]->(:movie {title:'The Matrix Reloaded'}) RETURN p AS b) y; ``` ``` equal | less -------+------ f | t ``` 그 비교에는 `tid` 필드도 들어가므로, 그 경로가 뜻하는 것을 비교하는 것이 아니라 보행 전체의 식별자를 비교하는 것입니다. 추론할 수 있는 것을 원하면 `nodes(p)`나 id를 비교하십시오. `to_jsonb()`는 Cypher에서든 SQL에서든 경로를 변환합니다. 그 결과는 내부 구조를 드러냅니다. 두 배열과 모든 구성원의 `tid`입니다. `pg_cast`에는 `graphpath`에서 `jsonb`로 가는 형 변환이 없습니다. 그래서 `p::jsonb`는 그것을 컴파일해 주는 Cypher에서는 동작하고, SQL에서는 `cannot cast type graphpath to jsonb`로 실패합니다. {#types-rowid} ## rowid `rowid`는 테이블 OID와 행 포인터를 한 쌍으로 묶습니다. 최단 경로 실행기가 간선 식별자를 나르려고 내부에서 씁니다. 비교 연산자 여섯 개와 btree 연산자 클래스가 있고, **출력 함수는 없습니다**: ```sql SELECT rowid('moviekb.person'::regclass, ctid) FROM moviekb.person LIMIT 1; ``` ``` ERROR: "rowid" type does not support output function ``` `rowid_tableoid()`와 `rowid_ctid()`가 두 절반을 읽으며, 그 값을 볼 수 있는 방법은 그것뿐입니다: ```sql SELECT rowid_tableoid(rowid('moviekb.person'::regclass, '(0,1)')) AS tableoid, rowid_ctid(rowid('moviekb.person'::regclass, '(0,1)')) AS ctid; ``` ``` tableoid | ctid ----------+------- 16482 | (0,1) ``` 그 OID는 해당 데이터베이스에서 `moviekb.person`이 우연히 갖게 된 값이므로, 설치할 때마다 다릅니다. Cypher에서 `rowid`를 만들어 내는 것은 없고, 애플리케이션 코드에서 그것을 쓸 이유도 없습니다. `pg_type`과 `\dT`에 나타나기 때문에 여기 문서화해 둔 것입니다. {#types-arrays-of-graph-types} ## 그래프 타입의 배열 `vertex[]`, `edge[]`, `graphpath[]`는 자기 출력 함수를 가진 평범한 PostgreSQL 배열 타입이므로, 그 요소와 같은 대괄호 방식으로 출력됩니다. 정점·간선·경로에 대한 `collect()`는 이런 네이티브 배열 중 하나를 반환합니다. 그 밖의 것에 대한 `collect()`는 `jsonb`를 반환합니다: ```cypher MATCH (:person {name: 'Keanu Reeves'})-[:acted_in]->(m:movie) RETURN pg_typeof(collect(m)) AS collect_vertex, pg_typeof(collect(m.title)) AS collect_property; ``` ``` collect_vertex | collect_property ----------------+------------------ "vertex[]" | "jsonb" ``` 네이티브 배열은 각 요소의 `jsonb` 사본이 아니라 요소 자체를 담으므로, 그 구성원이 자기 식별자를 유지합니다. `films[0]`은 `vertex`이고, 거기서 속성을 읽거나 뒤따르는 절에서 그것을 따라갈 수 있습니다. 리스트 함수 몇 개는 `jsonb`용 오버로드와 네이티브 배열용 오버로드를 함께 갖고 있습니다. 어느 것을 받는지는 인자에서 따라옵니다. Cypher에서 인자는 보통 `jsonb`이므로 결과도 `jsonb`입니다. SQL에서 같은 함수에 실제 배열을 주면 실제 배열이 돌아옵니다. 시그니처는 [타입 변환 함수](functions/type-conversion.md)에 있습니다. {#types-array-index-origin} ### 배열 첨자의 시작 Cypher는 리스트의 첨자를 0부터 세고 음수 첨자를 받으며, 그 규칙을 네이티브 배열에도 적용합니다: ```cypher MATCH (:person {name: 'Keanu Reeves'})-[:acted_in]->(m:movie) WITH collect(m) AS films RETURN films[0].title AS cypher_index_0, films[1].title AS cypher_index_1, size(films) AS n; ``` ``` cypher_index_0 | cypher_index_1 | n ----------------+-----------------------+--- "The Matrix" | "The Matrix Reloaded" | 4 ``` SQL에서 닿은 같은 배열은 PostgreSQL의 규칙을 따라 1부터 시작하고, 첨자 0은 범위를 벗어납니다: ```psql SELECT (films[1]).properties ->> 'title' AS sql_index_1, (films[0]).properties ->> 'title' AS sql_index_0 FROM (MATCH (:person {name: 'Keanu Reeves'})-[:acted_in]->(m:movie) RETURN collect(m) AS films) q; ``` ``` sql_index_1 | sql_index_0 -------------+------------- The Matrix | null ``` `unnest()`가 그것을 다시 행으로 바꾸며, SQL에서는 보통 그것이 원하는 것입니다: ```psql SELECT (v).id AS id, (v).properties ->> 'title' AS title FROM (MATCH (:person {name: 'Keanu Reeves'})-[:acted_in]->(m:movie) RETURN collect(m) AS films) q, unnest(q.films) AS v ORDER BY id; ``` ``` id | title -----+-------------------------- 3.1 | The Matrix 3.2 | The Matrix Reloaded 3.3 | The Matrix Revolutions 3.4 | The Matrix Resurrections ``` {#types-properties-jsonb} ## 속성: jsonb 레이블에는 `jsonb` 타입의 `properties` 컬럼이 하나 있고, 그 레이블의 모든 정점과 간선의 모든 속성이 그 안에 있습니다. 그래서 속성에는 선언된 타입이 없습니다. 그 타입은 값이 지닌 JSON 타입이 무엇이든 그것입니다. {#types-what-a-property-value-may-be} ### 속성 값이 될 수 있는 것 JSON의 여섯 타입 모두입니다: | JSON 타입 | Cypher 표기 | 비고 | | --- | --- | --- | | 문자열 | `'text'` | 작은따옴표입니다. 이스케이프는 JSON의 규칙을 따릅니다. | | 숫자 | `42`, `1.5`, `1e3` | JSON 숫자 타입 하나뿐이고, 정수와 실수가 따로 없습니다. | | 불리언 | `true`, `false` | | | 배열 | `[1, 'two', null]` | 타입을 섞을 수 있고 중첩됩니다. | | 객체 | `{a: {b: 1}}` | 키는 따옴표를 붙이지 않으면 소문자로 접힙니다. | | null | `null` | null을 할당하면 그 속성이 **지워집니다.** [`allow_null_properties`](#guc-allow_null_properties)를 켜 둔 경우는 예외입니다. | ```cypher MATCH (m:movie {title: 'The Matrix'})-[r:related_to]->() RETURN jsonb_typeof(m.title) AS title, jsonb_typeof(m.embedding) AS embedding, jsonb_typeof(r.weight) AS weight, jsonb_typeof(m.no_such_key) AS absent LIMIT 1; ``` ``` title | embedding | weight | absent ----------+-----------+----------+-------- "string" | "array" | "number" | null ``` 없는 속성과 null인 속성은 같은 것입니다. 그래서 없는 키에 대한 `jsonb_typeof`는 `"null"`이 아니라 null입니다. 숫자 타입은 하나이지만 적은 소수 자릿수가 저장되고, 그것이 나눗셈에서 버림이 일어나는지를 결정합니다: ```cypher CREATE GRAPH demo; SET graph_path = demo; CREATE VLABEL t; CREATE (:t {a: 7, b: 7.0}); MATCH (n:t) RETURN n.a AS a, n.b AS b, n.a / 2 AS a_div, n.b / 2 AS b_div; ``` ``` a | b | a_div | b_div ---+-----+-------+-------------------- 7 | 7.0 | 3 | 3.5000000000000000 ``` `n.a`와 `n.b`는 둘 다 `jsonb_typeof`가 `number`이고 같다고 비교되므로, 이것은 두 개의 타입이 아닙니다. 소수 자릿수를 몇 개 받았는지 기억하는 하나의 타입입니다. 선언된 타입이 없으므로, 타입이 서로 다른 값의 순서는 `jsonb` 비교가 결정합니다. null, 그다음 문자열, 그다음 숫자, 그다음 불리언, 그다음 배열, 그다음 객체입니다. 그 순서는 PostgreSQL의 것입니다. 타입 컬럼으로 승격한 속성이 같은 속성을 `properties`에서 읽은 것과 다르게 정렬될 수 있는 이유가 그것입니다. {#types-what-a-return-hands-back} ### RETURN이 돌려주는 것 Cypher의 `RETURN`은 각 스칼라 출력 컬럼을 `jsonb`로 감쌉니다. `boolean`은 불리언으로 남고, 그래프 타입은 자기 자신으로 남습니다: ```psql SELECT pg_typeof(a) AS property, pg_typeof(b) AS property_cast_to_text, pg_typeof(c) AS string_literal, pg_typeof(d) AS number_literal, pg_typeof(e) AS boolean, pg_typeof(f) AS id, pg_typeof(g) AS vertex, pg_typeof(h) AS path FROM (MATCH p = (:person {name: 'Keanu Reeves'})-[:acted_in]->(m:movie {title: 'The Matrix'}) RETURN m.title AS a, m.title::text AS b, 'x' AS c, 1 AS d, true AS e, id(m) AS f, m AS g, p AS h) q; ``` ``` property | property_cast_to_text | string_literal | number_literal | boolean | id | vertex | path ----------+-----------------------+----------------+----------------+---------+---------+--------+----------- jsonb | jsonb | jsonb | jsonb | boolean | graphid | vertex | graphpath ``` 두 번째 컬럼을 보십시오. `RETURN` 안의 `::text` 형 변환은 그 감싸기에 의해 무효가 되므로 아무것도 바꾸지 않습니다. 그래서 문자열은 JSON 따옴표와 함께 출력됩니다: ```cypher MATCH (m:movie {title: 'The Matrix'}) RETURN m.title AS title; ``` ``` title -------------- "The Matrix" ``` 평범한 스칼라를 얻으려면 SQL에서 그 값을 `jsonb`에서 꺼내십시오. `#>> '{}'`이 문서 전체를 텍스트로 꺼냅니다: ```psql SELECT title #>> '{}' AS title FROM (MATCH (m:movie {title: 'The Matrix'}) RETURN m.title AS title) q; ``` ``` title ------------ The Matrix ``` 감싸는 방법은 [SQL과 Cypher 섞기](../querying/combining-sql-and-cypher.md)가 온전히 다룹니다. 이 감싸기는 속성에서 끌어낸 컬럼에만 적용됩니다. 불리언 표현식은 `boolean`으로, `id(n)`은 `graphid`로, 그래프 요소는 `vertex`·`edge`·`graphpath`로 남습니다. 타입 컬럼으로 승격된 속성도 예외입니다. 그 읽기는 그 컬럼에서 답하고 그 컬럼 자신의 타입으로 돌아옵니다. {#types-the--cast} ### :: 형 변환 Cypher의 `::`은 SQL의 것보다 적은 타입 이름 집합을 받습니다. `text`, `date`, `uuid`, `jsonb`, `inet` 같은 맨 타입 이름이나 함수 이름을 받습니다. SQL 문법이 특별하게 다루는 묶음 표기도 받습니다. 수치 이름(`int`, `integer`, `smallint`, `bigint`, `real`, `float`, `double precision`, `decimal`, `numeric`, `boolean`)과 문자 이름(`char`, `varchar`, `character varying`), 그리고 `bit`, `bit varying`, `json`, `interval`입니다. ```cypher RETURN '42'::int AS i, '4.2'::numeric AS n, 'x'::text AS t, '101'::bit(3) AS b, '1 day'::interval AS iv, 'true'::boolean AS bo, '2020-01-01'::date AS d; ``` ``` i | n | t | b | iv | bo | d ----+-----+-----+-------+---------+----+-------------- 42 | 4.2 | "x" | "101" | "1 day" | t | "2020-01-01" ``` 이유는 형 변환이 타입을 어떻게 지칭하는가에 있습니다. Cypher의 형 변환은 평범한 타입 이름을 받습니다. 그리고 언어가 특별하게 다루는 수치·문자·비트·json·interval 표기도 받습니다. `TIMESTAMP`와 `TIME`도 특별한 표기인데, 빠진 것이 바로 그 둘입니다. 그래서 둘 다 거부됩니다: ```cypher RETURN '2020-01-01 10:00'::timestamp; ``` ``` ERROR: syntax error at or near "timestamp" LINE 1: RETURN '2020-01-01 10:00'::timestamp; ^ ``` ```cypher RETURN '10:00'::time; ``` ``` ERROR: syntax error at or near "time" LINE 1: RETURN '10:00'::time; ^ ``` 그 타입 이름에 따옴표를 붙이십시오. 인용된 이름은 키워드가 아니라 식별자이므로, 동작하는 규칙을 타고 갑니다. 같은 타입을 가리킵니다: ```cypher RETURN '2020-01-01 10:00'::"timestamp" AS ts, '10:00'::"time" AS t; ``` ``` ts | t -----------------------+------------ "2020-01-01T10:00:00" | "10:00:00" ``` 그 밖의 시간 관련 타입은 모두 평범한 이름이라 따옴표가 필요하지 않으며, `date`와 `timestamptz`도 그렇습니다: ```cypher RETURN pg_typeof('2020-01-01'::date) AS d, pg_typeof('2020-01-01'::timestamptz) AS tz; ``` ``` d | tz --------+---------------------------- "date" | "timestamp with time zone" ``` 다른 길은 Cypher 밖에서 형 변환하는 것이며, 거기서는 SQL 문법 전체를 쓸 수 있습니다: ```psql SELECT (t #>> '{}')::timestamp AS released FROM (RETURN '2020-01-01 10:00' AS t) q; ``` ``` released --------------------- 2020-01-01 10:00:00 ``` 두 결과의 차이를 보십시오. Cypher 안에서는 값이 다시 `jsonb`로 감싸이므로 JSON의 ISO 형태로 따옴표와 함께 출력됩니다. SQL 형 변환은 실제 `timestamp`를 주고, 그 타입의 출력 함수가 그것을 출력합니다. 알아 둘 비대칭이 하나 더 있습니다. 문자열 리터럴에 쓴 `'…'::jsonb`는 파싱된 문서가 아니라 `jsonb` **문자열**을 만듭니다. 그 리터럴이 이미 JSON 문자열이기 때문입니다. Cypher에서 `jsonb` 객체를 만들려면 맵 리터럴을 쓰십시오: ```cypher RETURN '{"a": 1}'::jsonb AS as_string, {a: 1} AS as_map; ``` ``` as_string | as_map --------------+---------- "{\"a\": 1}" | {"a": 1} ``` {#types-which-postgresql-types-you-can-use} ## 쓸 수 있는 PostgreSQL 타입 전부입니다. 그래프는 여러분의 테이블과 같은 데이터베이스에 있으므로, [PostgreSQL 데이터 타입 장](https://www.postgresql.org/docs/18/datatype.html)의 모든 타입을 그대로 쓸 수 있습니다. 수치, 문자, 날짜·시각, 불리언, 열거, 기하, 네트워크 주소, 비트 문자열, 텍스트 검색, UUID, XML, JSON, 배열, 복합, 범위, 도메인, 객체 식별자가 그렇습니다. 확장이 더한 타입도 쓸 수 있습니다. PostgreSQL 타입이 그래프와 만나는 자리가 세 곳입니다: * 그래프 옆의 **보통 테이블**이며, 한 문장에서 그래프와 조인합니다. `timestamptz`, `numeric(10,2)`, `tsvector`의 의미와 연산자가 필요할 때 그것들이 있어야 할 자리가 여기입니다. * 레이블의 **승격된 컬럼**입니다. 속성 하나를 실제 타입의 실제 컬럼으로 구체화하므로, 그 속성이 그 타입의 순서·비교·인덱스 지원을 얻습니다. * 속성이나 승격된 컬럼에 저장한 **확장 타입**입니다. `pgvector`의 `vector`가 [벡터 검색](../guides/search/vector.md)이 기반으로 삼는 경우입니다. 속성 자체는 그 타입들 중 어느 것도 될 수 없습니다. 속성은 `jsonb`이고, 그 안에 넣은 값은 들어가는 길에 JSON 타입으로 변환됩니다. {#types-compatibility} ## 호환성 {#types-opencypher} ### openCypher `vertex`, `edge`, `graphpath`가 openCypher 타입 체계의 `node`·`relationship`·`path`에 해당합니다. 여기서 스칼라 타입은 따로 있지 않습니다. 문자열·숫자·불리언·리스트·맵이 모두 `jsonb`입니다. 그래서 어떤 숫자가 정수인지 소수인지는 선언된 타입이 아니라 그 값에서 옵니다. 시간 값은 그래프 타입이 아닙니다. `date`, `timestamptz`, `interval`은 PostgreSQL의 것이고, 형 변환· 보통 컬럼·승격된 컬럼을 통해 닿습니다. 그래프 수준의 `datetime`이나 `duration`은 없습니다. 차이의 전체 목록은 [Cypher 호환성](../appendix/cypher-compatibility.md)입니다. {#types-isoiec-39075} ### ISO/IEC 39075 표준의 속성 그래프 타입도 같은 방식으로 `vertex`·`edge`·`graphpath`에 대응됩니다. 속성이 선언된 타입을 지니는 표준의 타입 있는 값 모형은 승격된 컬럼이 제공합니다. [GQL 적합성](../appendix/gql-conformance.md)을 참고하십시오. {#types-see-also} ## 관련 항목 - [구문](syntax.md) — 리터럴, 연산자, 그리고 값을 적는 방법 - [서버 설정](guc.md) — `allow_null_properties`와 `enable_property_promotion` - [그래프가 어떻게 저장되는가](../concepts/graph-on-postgresql.md) — 레이블 테이블은 무엇으로 되어 있는가 - [SQL과 Cypher 섞기](../querying/combining-sql-and-cypher.md) — 경계를 넘어 값을 옮기기 - [시스템 카탈로그](../system_catalogs/catalogs.md) — `ag_graph`, `ag_label` 등 - [PostgreSQL: 데이터 타입](https://www.postgresql.org/docs/18/datatype.html) — 그 밖에 쓸 수 있는 모든 타입