이 페이지에서는 Spanner Graph 내에서 스키마 없는 데이터를 관리하는 방법을 설명합니다. 또한 권장사항 및 문제 해결 도움말도 자세히 설명합니다. Spanner Graph 스키마 및 쿼리에 익숙한 것이 좋습니다.
스키마 없는 데이터 관리를 사용하면 스키마를 변경하지 않고도 노드 및 에지 유형 정의를 추가, 업데이트 또는 삭제할 수 있는 유연한 그래프 정의를 만들 수 있습니다. 이 접근 방식은 반복적인 개발과 적은 스키마 관리 오버헤드를 지원하면서도 익숙한 그래프 쿼리 환경을 유지합니다.
스키마 없는 데이터 관리는 다음과 같은 경우에 특히 유용합니다.
- 요소 라벨 및 속성의 업데이트 및 추가와 같이 자주 변경되는 그래프를 관리합니다.
- 그래프에 노드 및 에지 유형이 많아 입력 테이블을 만들고 관리하기가 어렵습니다.
스키마 없는 데이터 모델링
Spanner Graph를 사용하면 행이 노드 및 에지에 매핑된 테이블에서 그래프를 만들 수 있습니다. 스키마 없는 데이터 모델링은 일반적으로 각 요소 유형에 별도의 테이블을 사용하는 대신 단일 노드 테이블과 단일 에지 테이블을 사용하며, 라벨에는 STRING
열을, 속성에는 JSON
열을 사용합니다.
입력 테이블 만들기
다음 예시와 같이 단일 GraphNode
테이블과 단일 GraphEdge
테이블을 만들어 스키마 없는 데이터를 저장할 수 있습니다. 테이블 이름은 설명 목적으로 사용되며 원하는 이름을 선택할 수 있습니다.
CREATE TABLE GraphNode (
id INT64 NOT NULL,
label STRING(MAX) NOT NULL,
properties JSON,
) PRIMARY KEY (id);
CREATE TABLE GraphEdge (
id INT64 NOT NULL,
dest_id INT64 NOT NULL,
edge_id INT64 NOT NULL,
label STRING(MAX) NOT NULL,
properties JSON,
) PRIMARY KEY (id, dest_id, edge_id),
INTERLEAVE IN PARENT GraphNode;
이 예시에서는 다음을 수행합니다.
모든 노드를 단일 테이블
GraphNode
에 저장하며,id
로 고유하게 식별됩니다.소스(
id
), 대상(dest_id
), 자체 식별자(edge_id
)의 조합으로 고유하게 식별되는 단일 테이블GraphEdge
에 모든 에지를 저장합니다.edge_id
는id
에서dest_id
쌍으로의 두 개 이상의 에지를 허용하기 위해 기본 키의 일부로 포함됩니다.
노드 테이블과 에지 테이블 모두 각각 STRING
및 JSON
유형의 자체 label
및 properties
열을 갖습니다.
속성 그래프 만들기
CREATE PROPERTY GRAPH 문을 사용하면 이전 섹션의 입력 테이블이 노드 및 에지로 매핑됩니다. 스키마 없는 데이터의 라벨과 속성을 정의하려면 다음 절을 사용해야 합니다.
DYNAMIC LABEL
: 입력 테이블의STRING
열에서 노드 또는 에지의 라벨을 만듭니다.DYNAMIC PROPERTIES
: 입력 테이블의JSON
열에서 노드 또는 에지의 속성을 만듭니다.
다음 예시는 이러한 절을 사용하여 그래프를 만드는 방법을 보여줍니다.
CREATE PROPERTY GRAPH FinGraph
NODE TABLES (
GraphNode
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties)
)
EDGE TABLES (
GraphEdge
SOURCE KEY (id) REFERENCES GraphNode(id)
DESTINATION KEY (dest_id) REFERENCES GraphNode(id)
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties)
);
동적 라벨
DYNAMIC LABEL
절은 라벨 값을 저장할 STRING
데이터 유형 열을 지정합니다.
예를 들어 GraphNode
행에서 label
열에 person
값이 있으면 그래프 내의 Person
노드에 매핑됩니다. 마찬가지로 GraphEdge
행에서 라벨 열의 값이 owns
이면 그래프 내의 Owns
에지에 매핑됩니다.
동적 속성
DYNAMIC PROPERTIES
절은 속성을 저장할 JSON
데이터 유형 열을 지정합니다. JSON 키는 속성 이름이고 JSON 값은 속성 값입니다.
예를 들어 GraphNode
행의 properties
열에 JSON 값 '{"name": "David", "age": 43}'
이 있으면 age
및 name
속성이 있는 노드에 매핑되며, 이때 속성 값은 43
및 "David"
입니다.
스키마 없는 데이터 관리를 사용하지 않는 경우
다음과 같은 경우에는 스키마 없는 데이터 관리를 사용하지 않는 것이 좋습니다.
- 그래프 데이터의 노드 및 에지 유형이 잘 정의되어 있거나 라벨 및 속성을 자주 업데이트할 필요가 없는 경우
- 데이터가 이미 Spanner에 저장되어 있으며, 새로운 전용 노드 및 에지 테이블을 추가하는 대신 기존 테이블에서 그래프를 구성하고자 하는 경우
- 스키마 없는 데이터의 제한사항으로 인해 채택하지 못했습니다.
또한 워크로드가 쓰기 성능에 매우 민감한 경우, 특히 속성이 자주 업데이트되는 경우 JSON
유형의 동적 속성을 사용하는 것보다 STRING
또는 INT64
와 같은 기본 데이터 유형으로 스키마 정의 속성을 사용하는 것이 더 효과적입니다.
동적 데이터 라벨 및 속성을 사용하지 않고 그래프 스키마를 정의하는 방법에 관한 자세한 내용은 Spanner Graph 스키마 개요를 참조하세요.
스키마 없는 그래프 데이터 쿼리
Graph Query Language(GQL)를 사용하여 스키마가 없는 그래프 데이터를 쿼리할 수 있습니다. Spanner Graph 쿼리 개요 및 GQL 참조의 샘플 쿼리를 제한적으로 수정하여 사용할 수 있습니다.
라벨을 사용하여 노드와 에지 일치시키기
GQL에서 라벨 표현식을 사용하여 노드와 에지를 일치시킬 수 있습니다.
다음 쿼리는 라벨 열에 account
및 transfers
값이 있는 연결된 노드와 에지를 일치시킵니다.
GRAPH FinGraph
MATCH (a:Account {id: 1})-[t:Transfers]->(d:Account)
RETURN COUNT(*) AS result_count;
속성에 액세스
JSON
데이터 유형의 최상위 키와 값은 다음 예시의 age
및 name
와 같은 속성으로 모델링됩니다.
JSON document |
Properties |
|
|
|
다음 예시는 Person
노드에서 name
속성에 액세스하는 방법을 보여줍니다.
GRAPH FinGraph
MATCH (person:Person {id: 1})
RETURN person.name;
그러면 다음과 비슷한 결과가 반환됩니다.
JSON"Tom"
속성 데이터 유형 변환
속성은 JSON 데이터 유형의 값으로 처리됩니다. SQL 유형과 비교하는 경우와 같이 먼저 SQL 유형으로 변환해야 하는 경우도 있습니다.
다음 예시에서 쿼리는 다음과 같은 데이터 유형 변환을 실행합니다.
is_blocked
속성을 불리언 유형으로 변환하여 표현식을 평가합니다.order_number_str
속성을 문자열 유형으로 변환하고 리터럴 값"302290001255747"
과 비교합니다.order_number_str
을 정수로 안전하게 변환하기 위해 LAX_INT64 함수를 사용하며, 반환 유형은 정수입니다.
GRAPH FinGraph
MATCH (a:Account)-[t:Transfers]->()
WHERE BOOL(a.is_blocked) AND STRING(t.order_number_str) = "302290001255747"
RETURN LAX_INT64(t.order_number_str) AS order_number_as_int64;
그러면 다음과 비슷한 결과가 반환됩니다.
+-----------------------+
| order_number_as_int64 |
+-----------------------+
| 302290001255747 |
+-----------------------+
GROUP BY
및 ORDER BY
와 같은 절에서는 JSON 데이터 유형도 변환해야 합니다. 다음 예시에서는 city
속성을 문자열 유형으로 변환하여 그룹화에 사용할 수 있도록 합니다.
GRAPH FinGraph
MATCH (person:Person {country: "South Korea"})
RETURN STRING(person.city) as person_city, COUNT(*) as cnt
LIMIT 10
JSON 데이터 유형을 SQL 데이터 유형으로 변환하기 위한 도움말:
INT64
와 같은 엄격한 변환기는 엄격한 유형 및 값 검사를 실행합니다. 이는 JSON 데이터 유형이 알려져 있고 적용되는 경우에 권장됩니다(예: 스키마 제약 조건을 사용하여 속성 데이터 유형을 적용).LAX_INT64
와 같은 유연한 변환기는 가능하면 값을 안전하게 변환하고 변환할 수 없는 경우에는NULL
을 반환합니다. 엄격한 검사가 필요하지 않거나 유형을 적용하기 어려운 경우에 권장됩니다.
데이터 변환에 대한 자세한 내용은 문제 해결 도움말을 참조하세요.
속성 값으로 필터링
속성 필터에서 필터 파라미터는 JSON
데이터 유형의 값으로 취급됩니다. 예를 들어 다음 쿼리에서 is_blocked
는 JSON boolean
로, order_number_str
은 JSON string
으로 취급됩니다.
GRAPH FinGraph
MATCH (a:Account {is_blocked: false})-[t:Transfers {order_number_str:"302290001255747"}]->()
RETURN a.id AS account_id;
그러면 다음과 비슷한 결과가 반환됩니다.
+-----------------------+
| account_id |
+-----------------------+
| 7 |
+-----------------------+
필터 파라미터는 속성 유형 및 값과 일치해야 합니다. 예를 들어 order_number_str
필터 파라미터가 정수인 경우 속성이 JSON string
이므로 일치하는 항목이 없습니다.
GRAPH FinGraph
MATCH (a:Account {is_blocked: false})-[t:Transfers {order_number_str: 302290001255747}]->()
RETURN t.order_number_str;
중첩된 JSON 속성에 액세스
중첩된 JSON 키와 값은 속성으로 모델링되지 않습니다. 다음 예시에서 JSON 키 city
, state
, country
는 location
아래에 중첩되어 있으므로 속성으로 모델링되지 않습니다. 하지만 JSON 필드 액세스 연산자 또는 JSON 하위 스크립트 연산자를 사용하여 액세스할 수 있습니다.
JSON document |
Properties |
|
|
|
|
|
다음 예시에서는 JSON 필드 액세스 연산자를 사용하여 중첩된 속성에 액세스하는 방법을 보여줍니다.
GRAPH FinGraph
MATCH (person:Person {id: 1})
RETURN STRING(person.location.city);
그러면 다음과 비슷한 결과가 반환됩니다.
"New York"
스키마 없는 데이터 수정
Spanner Graph는 테이블의 데이터를 그래프 노드 및 에지에 매핑합니다. 입력 테이블 데이터를 변경하면 해당 그래프 데이터에 직접 변경이 발생합니다. 그래프 데이터 변형에 관한 자세한 내용은 Spanner Graph 데이터 삽입, 업데이트 또는 삭제를 참조하세요.
예시
이 섹션에서는 그래프 데이터를 생성, 업데이트, 삭제하는 방법의 예시를 제공합니다.
그래프 데이터 삽입
다음 예시에서는 person
노드를 삽입합니다. 라벨 및 속성 이름은 소문자를 사용해야 합니다.
INSERT INTO GraphNode (id, label, properties)
VALUES (4, "person", JSON'{"name": "David", "age": 43}');
그래프 데이터 업데이트
다음 예시에서는 Account
노드를 업데이트하고 JSON_SET
함수를 사용하여 is_blocked
속성을 설정합니다.
UPDATE GraphNode
SET properties = JSON_SET(
properties,
'$.is_blocked', false
)
WHERE label = "account" AND id = 16;
다음 예시에서는 새 속성 집합으로 person
노드를 업데이트합니다.
UPDATE GraphNode
SET properties = JSON'{"name": "David", "age": 43}'
WHERE label = "person" AND id = 4;
다음 예시에서는 JSON_REMOVE
함수를 사용하여 Account
노드에서 is_blocked
속성을 삭제합니다. 실행 후에는 다른 모든 기존 속성이 변경되지 않습니다.
UPDATE GraphNode
SET properties = JSON_REMOVE(
properties,
'$.is_blocked'
)
WHERE label = "account" AND id = 16;
그래프 데이터 삭제
다음 예시에서는 차단된 계정으로 이전된 Account
노드의 Transfers
에지를 삭제합니다.
DELETE FROM GraphEdge
WHERE label = "transfers" and id IN {
GRAPH FinGraph
MATCH (a:Account)-[:Transfers]->{1,2}(:Account {is_blocked: TRUE})
RETURN a.id
}
제한사항
이 섹션에는 스키마 없는 데이터 관리 사용과 관련된 제한사항이 나와 있습니다.
동적 라벨의 단일 테이블 요구사항
정의에 동적 라벨이 사용된 경우, 노드 테이블을 하나만 가질 수 있습니다. 이 제한은 에지 테이블에도 적용됩니다. 다음과 같은 경우는 허용되지 않습니다.
- 동적 라벨이 있는 노드 테이블을 다른 노드 테이블과 함께 정의하는 경우
- 동적 라벨이 있는 에지 테이블을 다른 에지 테이블과 함께 정의하는 경우
- 각각 동적 라벨을 사용하는 여러 개의 노드 테이블 또는 에지 테이블을 정의하는 경우
예를 들어 다음 코드는 동적 라벨이 있는 그래프 노드를 여러 개 만들려고 하기 때문에 실패합니다.
CREATE OR REPLACE PROPERTY GRAPH FinGraph
NODE TABLES (
GraphNodeOne
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties),
GraphNodeTwo
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties),
Account
LABEL Account PROPERTIES(create_time)
)
EDGE TABLES (
...
);
라벨 이름은 소문자여야 함
라벨 문자열 값은 일치하려면 소문자로 저장되어야 합니다. 애플리케이션 코드나 스키마 제약 조건을 사용하여 이 규칙을 적용하는 것이 좋습니다.
라벨 문자열 값은 소문자로 저장해야 하지만 쿼리에서 참조될 때는 대소문자를 구분하지 않습니다.
다음 예시는 소문자 값으로 라벨을 삽입하는 방법을 보여줍니다.
INSERT INTO GraphNode (id, label) VALUES (1, "account");
INSERT INTO GraphNode (id, label) VALUES (2, "account");
대소문자를 구분하지 않는 라벨을 사용하여 GraphNode
또는 GraphEdge
와 일치시킬 수 있습니다.
GRAPH FinGraph
MATCH (accnt:Account {id: 1})-[:Transfers]->(dest_accnt:Account)
RETURN dest_accnt.id;
속성 이름은 소문자여야 함
속성 이름은 소문자로 저장되어야 합니다. 애플리케이션 코드나 스키마 제약 조건을 사용하여 이 규칙을 적용하는 것이 좋습니다.
속성 이름은 소문자로 저장해야 하지만 쿼리에서 참조할 때는 대소문자를 구분하지 않습니다.
다음 예시에서는 소문자를 사용하여 name
및 age
속성을 삽입합니다.
INSERT INTO GraphNode (id, label, properties)
VALUES (25, "person", JSON '{"name": "Kim", "age": 27}');
쿼리 텍스트에서 속성 이름은 대소문자를 구분하지 않습니다. 예를 들어 Age
또는 age
를 사용하여 속성에 액세스할 수 있습니다.
GRAPH FinGraph
MATCH (n:Person {Age: 27})
RETURN n.id;
기타 제한사항
권장사항
이 섹션에서는 스키마 없는 데이터를 모델링하기 위한 권장사항을 설명합니다.
노드 및 에지의 기본 키 정의
노드의 키는 모든 그래프 노드에서 고유해야 합니다. 예를 들면 INT64
또는 문자열 UUID
열로 표시할 수 있습니다.
두 노드 사이에 여러 개의 에지가 있는 경우 에지의 고유 식별자를 도입해야 합니다. 스키마 예시에서는 애플리케이션 로직 INT64
edge_id
열을 사용합니다.
노드 및 에지 테이블의 스키마를 만들 때 값이 변경 불가능한 경우 label
열을 기본 키 열로 포함할 수 있습니다. 이렇게 하면 모든 키 열로 구성된 복합 키가 모든 노드 또는 에지에서 고유해야 합니다. 이 기법은 라벨로만 필터링되는 쿼리의 성능을 개선합니다.
기본 키 선택에 관한 자세한 내용은 기본 키 선택을 참조하세요.
자주 액세스하는 속성의 보조 색인
필터에 자주 사용되는 속성의 쿼리 성능을 높이려면 생성된 속성 열에 대해 보조 색인을 만든 다음 그래프 스키마와 쿼리에서 이를 사용하면 됩니다.
다음 예시에서는 person
노드의 GraphNode
테이블에 생성된 age
열을 추가합니다. person
라벨이 없는 노드의 경우 값은 NULL
입니다.
ALTER TABLE GraphNode
ADD COLUMN person_age INT64 AS
(IF (label = "person", LAX_INT64(properties.age), NULL));
그런 다음 person_age
에 대해 NULL FILTERED INDEX
를 만들고 로컬 액세스를 위해 이를 GraphNode
테이블에 인터리브 처리합니다.
CREATE NULL_FILTERED INDEX IdxPersonAge
ON GraphNode(id, label, person_age), INTERLEAVE IN GraphNode;
이제 GraphNode
테이블에 그래프 노드 속성으로 사용할 수 있는 새 열이 포함됩니다. 속성 그래프 정의에 이를 반영하려면 CREATE OR REPLACE PROPERTY GRAPH
문을 사용하세요. 이렇게 하면 정의가 다시 컴파일되고 새 person_age
열이 속성으로 포함됩니다.
다음 문은 정의를 다시 컴파일하고 새 person_age
열을 속성으로 포함합니다.
CREATE OR REPLACE PROPERTY GRAPH FinGraph
NODE TABLES (
GraphNode
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties)
)
EDGE TABLES (
GraphEdge
SOURCE KEY (id) REFERENCES GraphNode (id)
DESTINATION KEY (dest_id) REFERENCES GraphNode (id)
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties)
);
다음 예시에서는 색인이 생성된 속성을 사용하여 쿼리를 실행합니다.
GRAPH FinGraph
MATCH (person:Person {person_age: 43})
RETURN person.id, person.name;
원하는 경우 색인을 만든 후 ANALYZE
명령어를 실행하여 쿼리 최적화 도구가 최신 데이터베이스 통계로 업데이트되도록 할 수 있습니다.
데이터 무결성 제약조건 확인
Spanner는 검사 제약 조건과 같은 스키마 객체를 지원하여 라벨 및 속성 데이터 무결성을 적용합니다. 이 섹션에는 스키마 없는 데이터와 함께 사용할 수 있는 검사 제약 조건에 관한 권장사항이 나와 있습니다.
유효한 라벨 값 적용
정의되지 않은 라벨 값을 방지하려면 라벨 열 정의에서 NOT NULL
을 사용하는 것이 좋습니다.
CREATE TABLE GraphNode (
id INT64 NOT NULL,
label STRING(MAX) NOT NULL,
properties JSON,
) PRIMARY KEY (id);
소문자 라벨 값 및 속성 이름 적용
라벨 및 속성 이름은 소문자로 저장해야 하므로 다음 중 하나를 실행하는 것이 좋습니다.
- 애플리케이션 로직에서 검사를 시행합니다.
- 스키마에 확인 제약 조건을 만듭니다.
쿼리 시 라벨 및 속성 이름은 대소문자를 구분하지 않습니다.
다음 예시에서는 라벨이 소문자여야 하도록 GraphNode
테이블에 노드 라벨 제약조건을 추가합니다.
ALTER TABLE GraphNode ADD CONSTRAINT NodeLabelLowerCaseCheck
CHECK(LOWER(label) = label);
다음 예시에서는 에지 속성 이름에 검사 제약조건을 추가합니다. 검사는 JSON_KEYS
를 사용하여 최상위 키에 액세스합니다. COALESCE
는 JSON_KEYS
가 NULL
을 반환하면 출력을 빈 배열로 변환한 다음 각 키가 소문자인지 확인합니다.
ALTER TABLE GraphEdge ADD CONSTRAINT EdgePropertiesLowerCaseCheck
CHECK(NOT array_includes(COALESCE(JSON_KEYS(properties, 1), []), key->key<>LOWER(key)));
속성 존재 적용
라벨에 속성이 있는지 확인하는 제약조건을 만들 수 있습니다.
다음 예시에서 제약조건은 person
노드에 name
속성이 있는지 확인합니다.
ALTER TABLE GraphNode
ADD CONSTRAINT NameMustExistForPersonConstraint
CHECK (IF(label = 'person', properties.name IS NOT NULL, TRUE));
속성 고유성 적용
노드 또는 에지의 속성이 동일한 라벨이 지정된 노드 또는 에지에서 고유한지 확인하는 속성 기반 제약 조건을 만들 수 있습니다. 이렇게 하려면 속성의 생성된 열에 고유 색인을 사용하세요.
다음 예시에서 고유 색인은 결합된 name
및 country
속성이 모든 person
노드에 대해 고유한지 확인합니다.
PersonName
의 생성 열을 추가합니다.ALTER TABLE GraphNode ADD COLUMN person_name STRING(MAX) AS (IF(label = 'person', STRING(properties.name), NULL)) Hidden;
PersonCountry
의 생성 열을 추가합니다.ALTER TABLE GraphNode ADD COLUMN person_country STRING(MAX) AS (IF(label = 'person', STRING(properties.country), NULL)) Hidden;
PersonName
및PersonCountry
속성에 대해NULL_FILTERED
고유 색인을 만듭니다.CREATE UNIQUE NULL_FILTERED INDEX NameAndCountryMustBeUniqueForPerson ON GraphNode (person_name, person_country);
속성 데이터 유형 적용
다음 예시와 같이 라벨의 속성 값에 데이터 유형 제약조건을 사용하여 속성 데이터 유형을 적용할 수 있습니다. 이 예시에서는 JSON_TYPE
함수를 사용하여 person
라벨의 name
속성이 STRING
유형을 사용하는지 확인합니다.
ALTER TABLE GraphNode
ADD CONSTRAINT PersonNameMustBeStringTypeConstraint
CHECK (IF(label = 'person', JSON_TYPE(properties.name) = 'string', TRUE));
정의된 라벨과 동적 라벨 결합
Spanner를 사용하면 속성 그래프의 노드에 정의된 라벨(스키마에 정의됨)과 동적 라벨(데이터에서 파생됨)을 모두 적용할 수 있습니다. 이 유연성을 활용하려면 라벨을 맞춤설정하면 됩니다.
GraphNode
테이블 생성을 보여주는 다음 스키마를 살펴보세요.
CREATE OR REPLACE PROPERTY GRAPH FinGraph
NODE TABLES (
GraphNode
LABEL Entity -- Defined label
DYNAMIC LABEL (label) -- Dynamic label from data column 'label'
DYNAMIC PROPERTIES (properties)
);
여기서 GraphNode
에서 생성된 모든 노드에는 정의된 라벨 Entity
가 있습니다. 또한 각 노드에는 라벨 열의 값에 따라 결정되는 동적 라벨이 있습니다.
그런 다음 라벨 유형을 기반으로 노드를 일치시키는 쿼리를 작성할 수 있습니다. 예를 들어 다음 쿼리는 정의된 Entity
라벨을 사용하여 노드를 찾습니다.
GRAPH FinGraph
MATCH (node:Entity {id: 1}) -- Querying by the defined label
RETURN node.name;
이 쿼리는 정의된 라벨 Entity
를 사용하지만 일치하는 노드에는 데이터에 기반한 동적 라벨도 포함됩니다.
스키마 예시
이 섹션의 스키마 예시를 템플릿으로 사용하여 자체 스키마를 만들 수 있습니다. 주요 스키마 구성요소는 다음과 같습니다.
- 그래프 입력 테이블 만들기
- 속성 그래프 만들기
- 선택사항: 역방향 에지 순회 색인을 사용하여 역방향 순회 성능을 개선합니다.
- 선택사항: 라벨별 쿼리 성능을 개선하는 라벨 색인
- 선택사항: 소문자 라벨 및 속성 이름을 적용하는 스키마 제약 조건
다음 예시는 입력 테이블과 속성 그래프를 만드는 방법을 보여줍니다.
CREATE TABLE GraphNode (
id INT64 NOT NULL,
label STRING(MAX) NOT NULL,
properties JSON
) PRIMARY KEY (id);
CREATE TABLE GraphEdge (
id INT64 NOT NULL,
dest_id INT64 NOT NULL,
edge_id INT64 NOT NULL,
label STRING(MAX) NOT NULL,
properties JSON
) PRIMARY KEY (id, dest_id, edge_id),
INTERLEAVE IN PARENT GraphNode;
CREATE PROPERTY GRAPH FinGraph
NODE TABLES (
GraphNode
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties)
)
EDGE TABLES (
GraphEdge
SOURCE KEY (id) REFERENCES GraphNode(id)
DESTINATION KEY (dest_id) REFERENCES GraphNode(id)
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties)
);
다음 예시에서는 색인을 사용하여 역방향 에지 순회를 개선합니다. STORING (properties)
절에는 이러한 속성을 필터링하는 쿼리의 속도를 높이는 에지 속성 사본이 포함됩니다. 쿼리에 STORING (properties)
절이 도움이 되지 않는 경우 생략할 수 있습니다.
CREATE INDEX R_EDGE ON GraphEdge (dest_id, id, edge_id) STORING (properties),
INTERLEAVE IN GraphNode;
다음 예시에서는 라벨 색인을 사용하여 라벨별로 노드를 일치시키는 속도를 높입니다.
CREATE INDEX IDX_NODE_LABEL ON GraphNode (label);
다음 예시에서는 소문자 라벨 및 속성을 적용하는 제약 조건을 추가합니다. 마지막 두 예시에서는 JSON_KEYS
함수를 사용합니다. 원하는 경우 애플리케이션 로직에서 소문자 검사를 적용할 수 있습니다.
ALTER TABLE GraphNode ADD CONSTRAINT node_label_lower_case
CHECK(LOWER(label) = label);
ALTER TABLE GraphEdge ADD CONSTRAINT edge_label_lower_case
CHECK(LOWER(label) = label);
ALTER TABLE GraphNode ADD CONSTRAINT node_property_keys_lower_case
CHECK(
NOT array_includes(COALESCE(JSON_KEYS(properties, 1), []), key->key<>LOWER(key)));
ALTER TABLE GraphEdge ADD CONSTRAINT edge_property_keys_lower_case
CHECK(
NOT array_includes(COALESCE(JSON_KEYS(properties, 1), []), key->key<>LOWER(key)));
DML을 사용하여 동적 속성의 일괄 업데이트 최적화
JSON_SET
및 JSON_REMOVE
와 같은 함수를 사용하여 동적 속성을 수정하면 읽기-수정-쓰기 작업이 발생합니다. STRING
또는 INT64
유형의 속성을 업데이트하는 것보다 비용이 더 많이 들 수 있습니다.
워크로드에 DML을 사용하여 동적 속성을 일괄 업데이트하는 작업이 포함된 경우 다음 권장사항을 고려하여 성능을 개선하세요.
행을 개별적으로 처리하는 대신 단일 DML 문에서 여러 행을 업데이트합니다.
광범위한 키 범위를 업데이트할 때는 기본 키를 기준으로 영향을 받는 행을 그룹화하고 정렬합니다. 각 DML로 겹치지 않는 범위를 업데이트하면 잠금 경합이 줄어듭니다.
성능을 개선하기 위해 DML 문에서 하드코딩 대신 쿼리 파라미터를 사용하세요.
이러한 제안을 바탕으로 다음 예시에서는 단일 DML 문에 100개 노드의 is_blocked
속성을 업데이트합니다. 쿼리 매개변수에는 다음이 포함됩니다.
@node_ids
:ARRAY
파라미터에 저장된GraphNode
행의 키입니다. 해당하는 경우 DML 전반에서 그룹화하고 정렬하면 성능이 향상됩니다.@is_blocked_values
: 업데이트할 상응하는 값으로,ARRAY
파라미터에 저장됩니다.
UPDATE GraphNode
SET properties = JSON_SET(
properties,
'$.is_blocked',
CASE id
WHEN @node_ids[OFFSET(0)] THEN @is_blocked_values[OFFSET(0)]
WHEN @node_ids[OFFSET(1)] THEN @is_blocked_values[OFFSET(1)]
...
WHEN @node_ids[OFFSET(99)] THEN @is_blocked_values[OFFSET(99)]
END,
create_if_missing => TRUE)
WHERE id IN UNNEST(@node_ids)
문제 해결
이 섹션에서는 스키마 없는 데이터 관련 문제를 해결하는 방법을 설명합니다.
속성이 TO_JSON
결과에 두 번 이상 표시됨
문제
다음 노드는 birthday
및 name
속성을 JSON
열의 동적 속성으로 모델링합니다. 그래프 요소 JSON 결과에 birthday
및 name
의 중복 속성이 표시됩니다.
GRAPH FinGraph
MATCH (n: Person {id: 14})
RETURN SAFE_TO_JSON(n) AS n;
그러면 다음과 비슷한 결과가 반환됩니다.
{
…,
"properties": {
"birthday": "1991-12-21 00:00:00",
"name": "Alex",
"id": 14,
"label": "person",
"properties": {
"birthday": "1991-12-21 00:00:00",
"name": "Alex"
}
}
…
}
가능한 원인
기본적으로 기본 테이블의 모든 열은 속성으로 정의됩니다. TO_JSON
또는 SAFE_TO_JSON
을 사용하여 그래프 요소를 반환하면 중복 속성이 발생합니다. 이는 JSON
열(properties
)이 스키마 정의 속성인 반면 JSON
의 최상위 키는 동적 속성으로 모델링되기 때문입니다.
추천 솔루션
이 동작을 방지하려면 다음 예시와 같이 스키마에서 속성을 정의할 때 PROPERTIES ALL COLUMNS EXCEPT
절을 사용하여 properties
열을 제외합니다.
CREATE OR REPLACE PROPERTY GRAPH FinGraph
NODE TABLES (
GraphNode
PROPERTIES ALL COLUMNS EXCEPT (properties)
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties)
);
스키마가 변경된 후에는 JSON
데이터 유형의 반환된 그래프 요소에 중복이 없습니다.
GRAPH FinGraph
MATCH (n: Person {id: 1})
RETURN TO_JSON(n) AS n;
이 쿼리는 다음을 반환합니다.
{
…
"properties": {
"birthday": "1991-12-21 00:00:00",
"name": "Alex",
"id": 1,
"label": "person",
}
}
속성 값이 제대로 변환되지 않을 때 발생하는 일반적인 문제
다음 문제의 일반적인 해결 방법은 쿼리 표현식 내에서 속성을 사용할 때 항상 속성 값 변환을 사용하는 것입니다.
변환 없이 속성 값 비교
문제
No matching signature for operator = for argument types: JSON, STRING
가능한 원인
쿼리가 속성 값을 올바르게 변환하지 않습니다. 예를 들어 name
속성은 비교 시 STRING
유형으로 변환되지 않습니다.
GRAPH FinGraph
MATCH (p:Person)
WHERE p.name = "Alex"
RETURN p.id;
추천 솔루션
이 문제를 해결하려면 비교 전에 값 변환을 사용하세요.
GRAPH FinGraph
MATCH (p:Person)
WHERE STRING(p.name) = "Alex"
RETURN p.id;
그러면 다음과 비슷한 결과가 반환됩니다.
+------+
| id |
+------+
| 1 |
+------+
또는 속성 필터를 사용하여 값 변환이 자동으로 이루어지는 등식 비교를 간소화할 수 있습니다. 값 유형('Alex')은 JSON
의 속성 STRING
유형과 정확하게 일치해야 합니다.
GRAPH FinGraph
MATCH (p:Person {name: 'Alex'})
RETURN p.id;
그러면 다음과 비슷한 결과가 반환됩니다.
+------+
| id |
+------+
| 1 |
+------+
변환 없이 RETURN DISTINCT
속성 값 사용
문제
Column order_number_str of type JSON cannot be used in `RETURN DISTINCT
가능한 원인
다음 예시에서 order_number_str
는 RETURN DISTINCT
문에 사용되기 전에 변환되지 않았습니다.
GRAPH FinGraph
MATCH -[t:Transfers]->
RETURN DISTINCT t.order_number_str AS order_number_str;
추천 솔루션
이 문제를 해결하려면 RETURN DISTINCT
전에 값 변환을 사용하세요.
GRAPH FinGraph
MATCH -[t:Transfers]->
RETURN DISTINCT STRING(t.order_number_str) AS order_number_str;
그러면 다음과 비슷한 결과가 반환됩니다.
+-----------------+
| order_number_str|
+-----------------+
| 302290001255747 |
| 103650009791820 |
| 304330008004315 |
| 304120005529714 |
+-----------------+
변환 없이 그룹화 키로 사용되는 속성
문제
Grouping by expressions of type JSON is not allowed.
가능한 원인
다음 예시에서는 t.order_number_str
이 JSON 객체를 그룹화하는 데 사용되기 전에 변환되지 않습니다.
GRAPH FinGraph
MATCH (a:Account)-[t:Transfers]->(b:Account)
RETURN t.order_number_str, COUNT(*) AS total_transfers;
추천 솔루션
이 문제를 해결하려면 속성을 그룹화 키로 사용하기 전에 값 변환을 사용하세요.
GRAPH FinGraph
MATCH (a:Account)-[t:Transfers]->(b:Account)
RETURN STRING(t.order_number_str) AS order_number_str, COUNT(*) AS total_transfers;
그러면 다음과 비슷한 결과가 반환됩니다.
+-----------------+------------------+
| order_number_str | total_transfers |
+-----------------+------------------+
| 302290001255747 | 1 |
| 103650009791820 | 1 |
| 304330008004315 | 1 |
| 304120005529714 | 2 |
+-----------------+------------------+
변환 없이 정렬 키로 사용되는 속성
문제
ORDER BY does not support expressions of type JSON
가능한 원인
다음 예시에서 t.amount
결과 정렬에 사용되기 전에 변환되지 않습니다.
GRAPH FinGraph
MATCH (a:Account)-[t:Transfers]->(b:Account)
RETURN a.Id AS from_account, b.Id AS to_account, t.amount
ORDER BY t.amount DESC
LIMIT 1;
추천 솔루션
이 문제를 해결하려면 ORDER BY
절에서 t.amount
를 변환합니다.
GRAPH FinGraph
MATCH (a:Account)-[t:Transfers]->(b:Account)
RETURN a.Id AS from_account, b.Id AS to_account, t.amount
ORDER BY DOUBLE(t.amount) DESC
LIMIT 1;
그러면 다음과 비슷한 결과가 반환됩니다.
+--------------+------------+--------+
| from_account | to_account | amount |
+--------------+------------+--------+
| 20 | 7 | 500 |
+--------------+------------+--------+
변환 중 유형 불일치
문제
The provided JSON input is not an integer
가능한 원인
다음 예시에서 order_number_str
속성은 JSON STRING
데이터 유형으로 저장됩니다. INT64
로 변환하려고 하면 오류가 반환됩니다.
GRAPH FinGraph
MATCH -[e:Transfers]->
WHERE INT64(e.order_number_str) = 302290001255747
RETURN e.amount;
추천 솔루션
이 문제를 해결하려면 값 유형과 일치하는 정확한 값 변환기를 사용하세요.
GRAPH FinGraph
MATCH -[e:Transfers]->
WHERE STRING(e.order_number_str) = "302290001255747"
RETURN e.amount;
그러면 다음과 비슷한 결과가 반환됩니다.
+-----------+
| amount |
+-----------+
| JSON"200" |
+-----------+
또는 다음 예시와 같이 값을 대상 유형으로 변환할 수 있는 경우 유연한 변환기를 사용하세요.
GRAPH FinGraph
MATCH -[e:Transfers]->
WHERE LAX_INT64(e.order_number_str) = 302290001255747
RETURN e.amount;
그러면 다음과 비슷한 결과가 반환됩니다.
+-----------+
| amount |
+-----------+
| JSON"200" |
+-----------+
다음 단계
- JSON에 대해 자세히 알아보려면 JSON 데이터 수정 및 JSON 함수 목록을 참조하세요.
- Spanner Graph와 openCypher 비교
- Spanner Graph로 마이그레이션