{#jsonb-jsonb-operators-and-functions} # jsonb 연산자와 함수 속성은 `jsonb`**입니다.** 정점은 복합 타입 `(id graphid, properties jsonb)`이고, 간선에는 `start`와 `end`가 더해집니다. 그러므로 Cypher에서 읽는 모든 속성과 SQL에서 손대는 모든 속성 컬럼이 PostgreSQL `jsonb` 타입의 값입니다. 이 페이지의 다른 무엇보다 먼저 받아들여야 할 결과가 두 가지입니다. 문자열 속성은 따옴표와 함께 돌아오고, `jsonb` 연산자 전체가 그것에 적용됩니다. 예제는 [예제 데이터셋](../../get_started/example-dataset.md)에서 실행됩니다. {#jsonb-property-access-operators} ## 속성 접근 연산자 이 네 개는 왼쪽에 `jsonb` 값을 받습니다. 화살표가 겹친 둘은 `text`를 반환하고, 화살표가 하나인 둘은 `jsonb`를 반환합니다. | 연산자 | 오른쪽 피연산자 | 반환 타입 | 결과 | | --- | --- | --- | --- | | `->` | `text` 키 또는 `integer` 인덱스 | `jsonb` | 그 키나 인덱스에 있는 값 | | `->>` | `text` 키 또는 `integer` 인덱스 | `text` | 같은 값이며, 문자열은 따옴표가 벗겨진 상태 | | `#>` | `text[]` 경로 | `jsonb` | 그 경로에 있는 값 | | `#>>` | `text[]` 경로 | `text` | 같은 값이며, 문자열은 따옴표가 벗겨진 상태 | ```sql SELECT properties -> 'name' AS quoted, properties ->> 'name' AS plain FROM moviekb.person ORDER BY id LIMIT 1; ``` ``` quoted | plain ----------------+-------------- "Keanu Reeves" | Keanu Reeves ``` ```psql SELECT '{"a":{"b":[1,2]}}'::jsonb #> '{a,b}' AS at_path, '{"a":{"b":[1,2]}}'::jsonb #>> '{a,b,0}' AS at_path_text; ``` ``` at_path | at_path_text ---------+-------------- [1, 2] | 1 ``` {#jsonb-getting-an-unquoted-scalar-out} ## 따옴표 없는 스칼라로 꺼내기 Cypher의 `RETURN`은 스칼라 출력 컬럼을 `jsonb`로 감쌉니다. 그래서 `RETURN m.title`은 따옴표까지 포함해 `"Inception"`을 출력하고, `RETURN m.title::text`도 그렇습니다. 그 따옴표는 `psql`이 만든 것이 아니라 JSON의 일부입니다. 빠져나가는 길은 `#>> '{}'`입니다. 빈 경로가 문서 전체를 고르고, `#>>`이 그것을 `text`로 반환합니다. Cypher를 SQL로 감싸고 거기서 적용하십시오: ```cypher SELECT t AS boxed, t #>> '{}' AS unboxed FROM (MATCH (m:movie {title:'Inception'}) RETURN m.title AS t) q; ``` ``` boxed | unboxed -------------+----------- "Inception" | Inception ``` Cypher 결과를 평범한 `text`와 조인하거나 비교해야 할 때는 언제든 이 관용구를 꺼내 쓰십시오. 관계형 테이블과의 조인이 그런 경우입니다. {#jsonb-containment-and-existence-operators} ## 포함·존재 연산자 | 연산자 | 오른쪽 피연산자 | 참이 되는 조건 | | --- | --- | --- | | `@>` | `jsonb` | 왼쪽 문서가 오른쪽 문서를 포함할 때 | | `<@` | `jsonb` | 왼쪽 문서가 오른쪽 문서에 포함될 때 | | `?` | `text` | 그 키나 문자열 요소가 있을 때 | | `?|` | `text[]` | 그 키 중 하나라도 있을 때 | | `?&` | `text[]` | 그 키가 모두 있을 때 | ```psql SELECT properties @> '{"name":"Keanu Reeves"}' AS contains, properties ? 'born' AS has_born, properties ?& ARRAY['name','born'] AS has_both FROM moviekb.person ORDER BY id LIMIT 1; ``` ``` contains | has_born | has_both ----------+----------+---------- t | t | t ``` 속성 인덱스는 이 중 어느 것도 처리할 수 없습니다. 각각 키 하나와의 비교가 아니라 `properties` 전체에 대한 포함 검사이기 때문입니다. `properties`에 걸린 GIN 인덱스는 처리할 수 있습니다. {#jsonb-modification-operators} ## 변경 연산자 | 연산자 | 오른쪽 피연산자 | 결과 | | --- | --- | --- | | `\|\|` | `jsonb` | 두 문서를 병합한 것이며, 공유된 키에서는 오른쪽이 이깁니다 | | `-` | `text` 키 또는 `integer` 인덱스 | 그 키나 요소가 빠진 문서 | | `#-` | `text[]` 경로 | 그 경로의 값이 빠진 문서 | ```psql SELECT '{"a":1}'::jsonb || '{"b":2}'::jsonb AS merged, '{"a":1,"b":2}'::jsonb - 'a' AS without_a, '{"a":{"b":1}}'::jsonb #- '{a,b}' AS without_path; ``` ``` merged | without_a | without_path ------------------+-----------+-------------- {"a": 1, "b": 2} | {"b": 2} | {"a": {}} ``` Cypher에서는 이것들을 대신 절로 표기합니다. `SET n += {city:'Seoul'}`이 병합하고 `REMOVE n.city`가 키를 지웁니다. {#jsonb-arithmetic-operators-on-jsonb} ## jsonb에 대한 산술 연산자 PostgreSQL이 이미 `jsonb`에 대해 `-`와 `||`에 뜻을 주었으므로, AgensGraph는 그것을 산술에 다시 쓸 수 없었습니다. 그래서 이름을 백틱으로 감싼 연산자 여덟 개를 더했습니다. 속성에 `+`, `-`, `*`, `/`, `%`, `^`를 쓰면 Cypher 파서가 그것들을 내놓습니다. | 연산자 | 구현 함수 | OID | 뜻 | | --- | --- | --- | --- | | ``` `+` ``` | `jsonb_add` | 7176 | 덧셈. 문자열 둘이나 리스트 둘이면 이어 붙이기 | | ``` `-` ``` | `jsonb_sub` | 7178 | 뺄셈 | | ``` `*` ``` | `jsonb_mul` | 7180 | 곱셈 | | ``` `/` ``` | `jsonb_div` | 7182 | 나눗셈. 두 피연산자가 모두 정수면 정수 나눗셈 | | ``` `%` ``` | `jsonb_mod` | 7184 | 나머지 | | ``` `^` ``` | `jsonb_pow` | 7186 | 거듭제곱 | | ``` `+` ``` (접두) | `jsonb_uplus` | 7190 | 단항 플러스 | | ``` `-` ``` (접두) | `jsonb_uminus` | 7191 | 단항 마이너스 | 이 포크의 렉서에서 백틱은 연산자 문자이므로(`scan.l:369`), 이 이름들은 SQL에서도 입력할 수 있습니다. 두 피연산자 모두 `jsonb`여야 합니다: ```sql SELECT '"a"'::jsonb `+` '"b"'::jsonb AS strings, '[1]'::jsonb `+` '[2]'::jsonb AS lists; ``` ``` strings | lists ---------+-------- "ab" | [1, 2] ``` ```{note} Cypher의 산술은 평범한 연산자로 쓰고 — `n.a + n.b` — 파서가 이것들을 고르게 하십시오. 백틱 이름이 여기 있는 것은 `EXPLAIN` 계획이나 오류 메시지, `pg_operator` 목록에서 그것을 알아볼 수 있게 하려는 것입니다. 구현 함수를 직접 호출할 수도 있습니다. `jsonb_add('1'::jsonb, '2'::jsonb)`은 3을 반환합니다. ``` {#jsonb-ag_property_text} ## ag_property_text {bdg-secondary-line}`2.18에 추가` ``` ag_property_text ( properties jsonb, key text, expected_kind "char" ) -> text ``` 속성의 값을 `text`로 반환하지만, 저장된 값이 요구한 JSON 종류일 때만 그렇습니다. | 이름 | 타입 | 설명 | | --- | --- | --- | | `properties` | `jsonb` | 읽을 속성 맵입니다. | | `key` | `text` | 속성의 이름입니다. | | `expected_kind` | `"char"` | 숫자는 `'n'`, 문자열은 `'s'`, 불리언은 `'b'`입니다. | **고려할 점** - 이 함수가 타입 컬럼 속성 승격의 뒤를 받칩니다. `->>`은 값의 텍스트를 반환하고 텍스트에는 종류가 없으므로, 그것을 통해서는 문자열 `"5"`와 숫자 `5`를 구별할 수 없습니다. 정수 컬럼이라면 문자열을 담은 속성에 대해 5라고 답해 버립니다. 이 함수는 종류를 먼저 묻습니다. - 종류가 맞지 않으면 null을 반환하는 것이 아니라 **오류를 냅니다.** `property "n" holds a number`가 나오고 상세로 `A column promoting this property holds strings.` 가 따라옵니다. - 없는 속성, 또는 있으면서 JSON null인 속성은 null을 반환합니다. - 손으로 쓰도록 만든 함수가 아닙니다. 속성은 Cypher에서 `n.key`로, SQL에서 `properties ->> 'key'`로 읽으십시오. ```psql SELECT ag_property_text('{"name":"x","n":5}'::jsonb, 'n', 'n') AS number, ag_property_text('{"name":"x"}'::jsonb, 'missing', 's') AS absent; ``` ``` number | absent --------+-------- 5 | ``` {#jsonb-cypher_to_jsonb} ## cypher_to_jsonb ``` cypher_to_jsonb ( value anyelement ) -> jsonb ``` Cypher의 규칙으로 값을 `jsonb`로 감쌉니다. 그 규칙은 문자열 타입에 대해 `to_jsonb`의 규칙과 다릅니다. **고려할 점** - Cypher의 `RETURN`이 스칼라 출력 컬럼을 감쌀 때 넣는 함수가 이것입니다. 실행 계획에서 그 감싸기가 일어나는 지점에 나타납니다. - 엄격 함수이므로 null은 null로 남습니다. - 파서는 그래프 요소를 이 함수로 감싸기를 거부합니다. 그 거부 때문에 `coalesce(m, 0)`과 정점을 담은 `jsonb` 리스트가 실패합니다. 그 거부는 함수가 아니라 파서의 `coerce_to_jsonb()`에 있습니다. `vertex`에 대해 이름으로 직접 호출하면 이 함수는 `id`와 `tid`까지 포함한 복합 타입 전체를 반환하며, 그것은 `v::jsonb`가 주는 `properties`가 *아닙니다.* 그것을 원하면 [`vertex_to_jsonb`](#jsonb-vertex_to_jsonb)나 형 변환을 쓰십시오. ```sql SELECT cypher_to_jsonb('abc'::text) AS from_text, cypher_to_jsonb(42) AS from_integer; ``` ``` from_text | from_integer -----------+-------------- "abc" | 42 ``` {#jsonb-edge_to_jsonb} ## edge_to_jsonb ``` edge_to_jsonb ( e edge ) -> jsonb ``` 간선의 `properties`를 반환합니다. 형 변환 `e::jsonb`가 쓰는 함수가 이것이고, [`properties(e)`](#scalar-properties)와 같은 값을 반환합니다. **고려할 점** - 양 끝점은 포함되지 않고 속성만 포함됩니다. 끝점은 [`start_id`](#scalar-start_id)와 [`end_id`](#scalar-end_id)로 읽으십시오. ```cypher MATCH ()-[e:related_to]->() WITH e LIMIT 1 RETURN edge_to_jsonb(e) AS bag; ``` ``` bag --------------- {"weight": 1} ``` {#jsonb-graphid_in_jsonb_array} ## graphid_in_jsonb_array ``` graphid_in_jsonb_array ( id graphid, elements jsonb ) -> boolean ``` `graphid`가 `jsonb` 배열에 담긴 그래프 요소 중 하나이면 참을 반환합니다. **고려할 점** - 파서가 `id(n) IN [...]`에 대해 이 함수를 내놓아서, 요소 목록을 하나하나 벗겨 내지 않고도 검사할 수 있게 합니다. `graphid`는 `jsonb` 문자열로 감싸이고, 이 함수는 그 형태와 비교합니다. - 손으로 쓰면 평범한 소속 검사입니다. ```sql SELECT graphid_in_jsonb_array('4.1'::graphid, '["4.1","4.2"]'::jsonb) AS present; ``` ``` present --------- t ``` {#jsonb-jsonb_array_length} ## jsonb_array_length ``` jsonb_array_length ( list jsonb ) -> integer ``` `jsonb` 배열의 요소 개수를 `integer`로 반환합니다. **고려할 점** - PostgreSQL 자신의 함수입니다. [`cardinality`](#list-cardinality)와 [`length`](#scalar-length)가 Cypher 쪽 표기이고 `jsonb`를 반환합니다. 이 함수는 네이티브 `integer`를 반환하고, 배열이 아닌 값에 대해서는 오류를 냅니다. ```sql SELECT jsonb_array_length('[1,2,3]'); ``` ``` jsonb_array_length -------------------- 3 ``` {#jsonb-to_jsonb} ## to_jsonb ``` to_jsonb ( value anyelement ) -> jsonb ``` 어떤 SQL 값이든 `jsonb`로 변환하며, 복합 타입은 객체로, 배열은 배열로 대응시킵니다. **고려할 점** - 관계형 행을 그래프로 들여오는 방법이 이것입니다. `LOAD FROM t AS r CREATE (:label = to_jsonb(row_to_json(r)))`가 각 행을 `properties`로 바꿉니다. - 그래프 요소에 대해서는 `properties`가 아니라 복합 타입 전체를 변환합니다. `properties`를 주는 것은 `v::jsonb`와 [`vertex_to_jsonb`](#jsonb-vertex_to_jsonb)입니다. ```sql SELECT to_jsonb(42) AS number, to_jsonb('x'::text) AS string; ``` ``` number | string --------+-------- 42 | "x" ``` {#jsonb-vertex_to_jsonb} ## vertex_to_jsonb ``` vertex_to_jsonb ( v vertex ) -> jsonb ``` 정점의 `properties`를 반환합니다. 형 변환 `v::jsonb`가 쓰는 함수가 이것이고, [`properties(v)`](#scalar-properties)와 같은 값을 반환합니다. ```cypher MATCH (n:person {name:'Keanu Reeves'}) RETURN vertex_to_jsonb(n) AS bag; ``` ``` bag ---------------------------------------- {"born": 1964, "name": "Keanu Reeves"} ``` {#jsonb-ordering-and-comparison} ## 순서와 비교 `jsonb`에는 자기만의 전순서가 있고, 그것은 값들이 자기 타입에서 가질 순서가 아닙니다. 종류가 먼저 옵니다. null, 그다음 문자열, 그다음 숫자, 그다음 불리언, 그다음 배열, 그다음 객체입니다. 같은 종류의 값만 값으로 비교됩니다: ```psql SELECT v FROM (VALUES ('1'::jsonb),('"a"'),('true'),('null'),('[1]'),('{"k":1}')) t(v) ORDER BY v; ``` ``` v ---------- null "a" 1 true [1] {"k": 1} ``` Cypher 결과를 정렬하거나 집계하는 곳이면 어디서나 이것이 문제가 됩니다. `jsonb` 컬럼에 대한 `min()`과 `max()`가 그 순서를 따르므로, 어떤 행에서는 숫자를 담고 어떤 행에서는 문자열을 담는 속성은 읽는 사람이 기대하는 대로 정렬되지 않습니다. 순서가 그 타입 자신의 것이어야 한다면 타입 컬럼을 통해 읽거나 형 변환을 하십시오. 정수 둘도 정수로 나뉩니다. `jsonb`가 그 구별을 유지하기 때문입니다: ```cypher RETURN 7 / 2 AS integer_division, 7.0 / 2 AS real_division; ``` ``` integer_division | real_division ------------------+-------------------- 3 | 3.5000000000000000 ``` {#jsonb-see-also} ## 관련 항목 - [모든 함수](all-functions.md) — 모든 함수를 한 표에 - [스칼라 함수](scalar.md) — `properties`, `keys`, `length` - [타입 변환 함수](type-conversion.md) — `to…` 계열 - [SQL과 Cypher 섞기](../../querying/combining-sql-and-cypher.md) — `#>> '{}'`이 값어치를 하는 곳