이 페이지에서는 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;
이 예에서는 다음 작업을 수행합니다.
고유한
id
로 식별되는 단일 테이블GraphNode
에 모든 노드를 저장합니다.소스(
id
), 대상(dest_id
), 자체 식별자(edge_id
)의 고유한 조합으로 식별되는 단일 테이블GraphEdge
에 모든 에지를 저장합니다.edge_id
는id
에서dest_id
쌍으로의 두 개 이상의 에지를 허용하기 위해 기본 키의 일부로 포함됩니다.
노드 테이블과 에지 테이블 모두 자체 label
및 properties
열이 있습니다.
이러한 열은 각각 STRING
및 JSON
유형입니다.
스키마 없는 데이터 관리를 위한 키 선택에 관한 자세한 내용은 노드 및 에지의 기본 키 정의를 참조하세요.
속성 그래프 만들기
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}'
이 있으면 Spanner는 age
및 name
속성이 있고 각 속성 값이 43
및 "David"
인 노드에 매핑합니다.
스키마 없는 데이터 관리 고려사항
다음과 같은 경우에는 스키마 없는 데이터 관리를 사용하지 않는 것이 좋습니다.
- 그래프 데이터의 노드 및 에지 유형이 잘 정의되어 있거나 라벨 및 속성을 자주 업데이트할 필요가 없는 경우
- 데이터가 이미 Spanner에 저장되어 있으며, 새로운 전용 노드 및 에지 테이블을 추가하는 대신 기존 테이블에서 그래프를 구성하고자 하는 경우
- 스키마 없는 데이터의 제한사항으로 인해 채택이 어려운 경우
또한 워크로드가 쓰기 성능에 매우 민감한 경우, 특히 속성이 자주 업데이트되는 경우 STRING
또는 INT64
와 같은 기본 데이터 유형으로 스키마 정의 속성을 사용하는 것이 JSON
유형으로 동적 속성을 사용하는 것보다 효과적입니다.
동적 데이터 라벨 및 속성을 사용하지 않고 그래프 스키마를 정의하는 방법에 관한 자세한 내용은 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;
속성에 액세스
Spanner는 JSON
데이터 유형의 최상위 키와 값을 다음 예시의 age
및 name
과 같은 속성으로 모델링합니다.
JSON document |
Properties |
|
|
|
다음 예시는 Person
노드에서 name
속성에 액세스하는 방법을 보여줍니다.
GRAPH FinGraph
MATCH (person:Person {id: 1})
RETURN person.name;
이 쿼리는 다음과 유사한 결과를 반환합니다.
JSON"Tom"
속성 데이터 유형 변환
Spanner는 속성을 JSON 데이터 유형의 값으로 취급합니다. SQL 유형과의 비교와 같은 경우에는 먼저 속성을 SQL 유형으로 변환해야 합니다.
다음 예시에서 쿼리는 다음과 같은 데이터 유형 변환을 실행합니다.
is_blocked
속성을 불리언 유형으로 변환하여 표현식을 평가합니다.order_number_str
속성을 문자열 유형으로 변환하고 리터럴 값"302290001255747"
과 비교합니다.- LAX_INT64 함수를 사용하여
order_number_str
을 반환 유형으로 정수로 안전하게 변환합니다.
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
을 반환합니다. 엄격한 확인이 필요하지 않거나 유형을 적용하기 어려운 경우 유연한 변환기를 사용하세요.
데이터 변환에 대한 자세한 내용은 문제 해결 도움말을 참고하세요.
속성 값으로 필터링
속성 필터에서 Spanner는 필터 파라미터를 JSON
데이터 유형의 값으로 취급합니다. 예를 들어 다음 쿼리에서 Spanner는 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
이므로 Spanner에서 일치하는 항목을 찾지 못합니다.
GRAPH FinGraph
MATCH (a:Account {is_blocked: false})-[t:Transfers {order_number_str: 302290001255747}]->()
RETURN t.order_number_str;
중첩된 JSON 속성에 액세스
Spanner는 중첩된 JSON 키와 값을 속성으로 모델링하지 않습니다.
다음 예시에서 Spanner는 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
}
알려진 제한사항
이 섹션에는 스키마 없는 데이터 관리 사용의 제한사항이 나와 있습니다.
동적 라벨의 단일 테이블 요구사항
정의에 동적 라벨이 사용된 경우, 노드 테이블을 하나만 가질 수 있습니다. 이 제한사항은 에지 테이블에도 적용됩니다. Spanner에서는 다음이 허용되지 않습니다.
- 동적 라벨이 있는 노드 테이블을 다른 노드 테이블과 함께 정의하는 경우
- 동적 라벨이 있는 에지 테이블을 다른 에지 테이블과 함께 정의하는 경우
- 각각 동적 라벨을 사용하는 여러 개의 노드 테이블 또는 에지 테이블을 정의하는 경우
예를 들어 다음 코드는 동적 라벨이 있는 그래프 노드를 여러 개 만들려고 하기 때문에 실패합니다.
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));
그러면 다음 DDL 문은 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));
고유한 속성 적용
노드 또는 에지의 속성이 동일한 라벨이 지정된 노드 또는 에지에서 고유한지 확인하는 속성 기반 제약 조건을 만듭니다. 이렇게 하려면 속성의 생성된 열에 대해 UNIQUE INDEX를 사용합니다.
다음 예시에서 고유 색인은 결합된 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
결과에 여러 번 표시됨
문제
다음 노드는 JSON
열에서 birthday
및 name
속성을 동적 속성으로 모델링합니다. 중복된 birthday
및 name
속성이 그래프 요소 JSON 결과에 표시됩니다.
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로 마이그레이션