Graph Query¶
AkasicDB에서 그래프 질의를 수행하는 방법을 안내합니다.
그래프 준비¶
테이블 준비는 링크를 참고해주세요.
-- 그래프 'retail_graph'를 정의
SELECT akasicdb.define_graph('retail_graph');
-- 그래프 정의 'retail_graph'에 정점 정의 'v_item', 'v_customer', 'v_store' 추가
SELECT akasicdb.define_vertex(
'retail_graph',
'v_item',
ARRAY['i_no integer', 'name varchar(50)', 'price decimal(7,2)'],
'item'
);
SELECT akasicdb.define_vertex(
'retail_graph',
'v_customer',
ARRAY['c_id integer', 'first_name varchar(20)', 'last_name varchar(30)'],
'customer'
);
SELECT akasicdb.define_vertex(
'retail_graph',
'v_store',
ARRAY['s_id integer', 'name varchar(50)'],
'store',
'SELECT s_id, name FROM store'
);
-- 그래프 정의 'retail_graph'에 간선 정의 'buy', 'sell' 추가
SELECT akasicdb.define_edge(
'retail_graph',
'buy',
'v_customer', 'v_item',
null,
'SELECT null FROM customer c, orders o, item i '
'WHERE c.c_id = o.c_id AND i.i_no = o.i_no',
'customer c', 'item i'
);
SELECT akasicdb.define_edge(
'retail_graph',
'sell',
'v_store', 'v_item',
null,
'SELECT null FROM store s, orders o, item i '
'WHERE s.s_id = o.s_id AND i.i_no = o.i_no',
'store s', 'item i'
);
-- 그래프 정의 'retail_graph'에 간선 정의 'co_purchase' 추가
SELECT akasicdb.define_edge(
'retail_graph',
'co_purchase',
'v_customer', 'v_customer',
null,
'SELECT null '
'FROM customer c1, orders o1, item i, orders o2, customer c2 '
'WHERE c1.c_id = o1.c_id AND i.i_no = o1.i_no '
'AND c2.c_id = o2.c_id AND i.i_no = o2.i_no',
'customer c1', 'customer c2'
);
-- 그래프 정의 'retail_graph'를 기반으로 그래프 생성
SELECT akasicdb.create_graph('retail_graph');
SQL/GQL 질의 인터페이스¶
SQL/GQL이란?¶
SQL/GQL은 SQL을 통해 그래프 질의를 실행하는 AkasicDB의 인터페이스입니다. akasicdb.cypher() 함수를 통해 제공되며, openCypher 스타일의 MATCH 문법과 일부 PostgreSQL 표현식, 타입, 집계 함수 및 연산자를 결합한 AkasicDB 고유의 그래프 질의 방언을 처리합니다.
SQL/GQL은 ISO GQL과 openCypher의 문법을 참고하지만 어느 쪽도 그대로 구현하지 않습니다. 따라서 SQL/GQL이라는 명칭을 ISO GQL 준수 선언으로 해석해서는 안 됩니다. AkasicDB는 ISO GQL 적합성을 주장하지 않으며 특정 openCypher 버전과의 호환성도 보장하지 않습니다. GRAPH_TABLE과 속성 그래프 DDL 같은 SQL/PGQ 기능도 지원하지 않습니다. AkasicDB 그래프 질의는 이 페이지에 명시된 문법과 동작을 기준으로 작성해 주세요.
akasicdb.cypher()는 PostgreSQL 테이블 소스이므로 반환된 스칼라 값을 SQL로 후처리하거나, 관계형 테이블 또는 다른 그래프 질의와 조인하거나, 외부 SQL 집합 연산에 사용할 수 있습니다. Vector Features - Querying에서 설명하는 벡터 연산자도 피연산자 타입이 호환되고 해당 연산자가 설치되어 있으면 그래프 질의에서 사용할 수 있습니다.
함수 시그니처와 SQL 호출 요구 사항¶
함수 인자 설명
akasicdb.cypher()의 기본 사용법:
akasicdb.cypher()에 파라미터를 포함한 형태:
akasicdb.cypher(
graph_name NAME, -- 질의를 수행할 그래프 이름
query_string TEXT, -- 수행할 그래프 질의
parameters JSONB -- JSON 형식의 파라미터 값
);
실제 SQL 호출에는 다음과 같이 외부 FROM 절과 컬럼 정의 목록도 지정해야 합니다.
SELECT *
FROM akasicdb.cypher('retail_graph', $$
MATCH (c:v_customer)
RETURN c.first_name
$$) AS (
first_name varchar
);
SQL에서 함수를 호출할 때는 다음 요구 사항을 따라야 합니다.
akasicdb.cypher()는FROM절의 테이블 소스로 호출해야 합니다.SELECT akasicdb.cypher(...)처럼 스칼라 표현식으로 호출하는 방식은 지원하지 않습니다.query_string은 PostgreSQL의 달러 인용 문자열로 작성해야 합니다.$$...$$와$query$...$query$같은 태그 형식을 모두 사용할 수 있으며, 작은따옴표로 묶은 일반 문자열은 사용할 수 없습니다.- 모든 호출에
AS (name type, ...)컬럼 정의 목록을 추가해야 합니다. 반환 타입이record이므로 선언한 컬럼 수는 그래프 질의의RETURN절이 생성하는 결과 컬럼 수와 일치해야 합니다. 결과 컬럼과AS컬럼은 위치에 따라 대응합니다. - AkasicDB는 각 반환 값을 PostgreSQL의 명시적 타입 캐스팅 규칙에 따라
AS에 선언된 타입으로 변환합니다. 정점, 간선 또는 경로 객체를 나타내는 SQL 타입은 제공하지 않으므로 개별 스칼라 속성을 반환해야 합니다.
세 번째 인자는 선택 사항이며 그래프 질의 파라미터를 담은 JSONB 객체를 받습니다. 추출된 파라미터 값은 기본적으로 TEXT로 제공되며 ::integer와 같은 PostgreSQL 스타일 문법으로 캐스팅할 수 있습니다. JSON 객체와 배열은 JSON 텍스트로 직렬화됩니다.
질의 지원 개요¶
다음 표는 일반적으로 사용하는 지원 문법을 간략히 정리한 것으로 전체 목록은 아닙니다. 자세한 제약 사항은 아래의 예시와 "SQL/GQL 사용 시 유의사항"을 참고해 주세요. 이 페이지에 지원한다고 명시되지 않은 문법은 지원된다고 가정하지 마세요.
| 영역 | 지원 동작 |
|---|---|
| 패턴 | 정점 변수가 처음 등장할 때 정확히 하나의 레이블을 지정해야 하며, 같은 변수가 다시 등장할 때는 레이블을 생략할 수 있습니다. 정방향(->) 및 역방향(<-) 탐색과 여러 고정 길이 관계로 이루어진 경로를 지원합니다. |
| 읽기 | MATCH, WHERE의 속성 조건, WITH 뒤에 이어지는 추가 MATCH 절 |
| 프로젝션 | WITH와 RETURN에서 스칼라 속성 및 PostgreSQL 스칼라 표현식 사용 |
| 결과 제어 | DISTINCT, 해석 가능한 표현식을 대상으로 하는 ORDER BY(ASC, DESC, ASCENDING, DESCENDING 지원), SKIP, LIMIT. WITH에서는 프로젝션 별칭과 이러한 기능을 함께 사용할 수 있습니다. |
| 집계 | count(*), count(DISTINCT ...), array_agg(), 그리고 PostgreSQL이 인자 타입에 맞게 해석할 수 있는 sum(), avg(), min(), max() 등의 집계 함수 |
| 가변 길이 탐색 | 범위가 제한된 VLE와 상한 또는 하한이 생략된 VLE. 자세한 탐색 동작은 VLE(Variable Length Edge) 패턴을 참고하세요. |
| 파라미터와 타입 | 선택적인 JSONB 인자의 $parameter 값과 PostgreSQL 스타일 ::type 캐스팅 |
| SQL 결합 | LEFT JOIN을 포함한 외부 SQL 조인 및 UNION과 같은 외부 SQL 집합 연산 |
| 벡터 표현식 | 피연산자 타입이 호환되고 해당 연산자가 설치되어 있을 때 <->, <=>, <#>와 같은 PostgreSQL 연산자 |
Basic Traversal¶
-- 이름이 Margaret인 소비자의 풀네임을 조회
SELECT *
FROM akasicdb.cypher('retail_graph', $$
MATCH (c:v_customer)
WHERE c.first_name = 'Margaret'
RETURN
c.first_name as first_name,
c.last_name as last_name
$$) as (
first_name varchar,
last_name varchar
);
-- Margaret Farias와 같은 상품을 구매한 다른 소비자의 이름을 조회
SELECT *
FROM akasicdb.cypher('retail_graph', $$
MATCH (c1:v_customer)-[:buy]->(i:v_item)<-[:buy]-(c2:v_customer)
WHERE c1.first_name = 'Margaret'
AND c1.last_name = 'Farias'
AND c1.vertex_id <> c2.vertex_id
RETURN
c2.first_name as first_name,
c2.last_name as last_name
$$) as (
first_name varchar,
last_name varchar
);
WITH Clause¶
-- Margaret Farias가 상품을 구매한 상점에서 판매하는 다른 상품들을 조회
SELECT item_name
FROM akasicdb.cypher('retail_graph', $$
MATCH (c:v_customer)-[:buy]->(i1:v_item)<-[:sell]-(s1:v_store)
WHERE c.first_name = 'Margaret'
AND c.last_name = 'Farias'
WITH DISTINCT s1.vertex_id AS store_id
MATCH (s2:v_store)-[:sell]->(i2:v_item)
WHERE s2.vertex_id = store_id
RETURN
DISTINCT i2.name AS item_name
$$) as (
item_name varchar
);
Aggregation¶
-- Margaret Farias가 특정 상점에서 구매한 상품의 이름과 구매한 수량을 조회
SELECT item_name, cnt
FROM akasicdb.cypher('retail_graph', $$
MATCH (c:v_customer)-[:buy]->(i:v_item)<-[:sell]-(s:v_store)
WHERE s.s_id = 1
AND c.first_name = 'Margaret'
AND c.last_name = 'Farias'
RETURN
i.name AS item_name,
count(i.name) AS cnt
$$) as (
item_name varchar,
cnt integer
);
VLE(Variable Length Edge) 패턴¶
-- co_purchase 간선을 통해 비슷한 구매 이력을 가진 소비자의 네트워크를 구축했을 때,
-- Latisha Hamilton으로부터 거리 1~3 이내의 소비자를 조회
SELECT *
FROM akasicdb.cypher('retail_graph', $$
MATCH (c1:v_customer)-[:co_purchase*1..3 SHORTEST]->(c2:v_customer)
WHERE c1.first_name = 'Latisha' AND c1.last_name = 'Hamilton'
RETURN
c2.first_name as first_name,
c2.last_name as last_name
$$) as (
first_name varchar,
last_name varchar
);
VLE는 *, *n, *m..n, *..n, *m.., *.. 형식을 지원하며 0-hop 범위도 사용할 수 있습니다. 범위 값은 음수일 수 없고 하한은 상한보다 클 수 없습니다.
VLE(Variable Length Edge)와 TC(Transitive Closure) 패턴에는 다음 동작과 제한 사항이 적용됩니다.
- 하나의 간선 레이블만 사용할 수 있으며, 해당 간선은 레이블이 같은 정점들을 연결해야 합니다.
- 하나의 VLE에서는 동일한 간선을 반복해서 지날 수 없지만 같은 정점은 다시 방문할 수 있습니다. 따라서 단순 경로(simple path)가 아닌 트레일(trail), 즉 간선 단순(edge-simple) 탐색입니다.
- 하나의 경로에 간선 패턴이 여러 개 포함된 경우, 서로 다른 간선 패턴에서는 같은 간선을 다시 지날 수 있습니다.
SHORTEST는 너비 우선 탐색을 사용하며, hop 범위 안에서 각 도착 정점에 대해 조건을 충족하는 경로 중 가장 먼저 발견한 경로 하나를 반환합니다. 이 동작이 ISO GQL 또는 openCypher의 최단 경로 구현과 동일하다고 가정하지 마세요.shortestPath()는 동일한 탐색 모드로 처리되며 인자에는 가변 길이 관계를 하나만 지정할 수 있습니다.- 탐색을 시작하는 정점(위 예시의
c1)을 필터링하지 않으면 탐색이 매우 느려질 수 있습니다. - 탐색할 hop 수가 늘어나면 성능이 저하될 수 있습니다. 각 도착 정점에 대해 조건을 충족하는 경로 중 처음 발견한 경로가 필요한 경우에만
SHORTEST를 사용해 주세요.
그래프 질의 내에서 파라미터 사용하기¶
-- 파라미터(customer_name, 값: Margaret)와 같은 이름을 가진 소비자의 풀네임을 조회
SELECT *
FROM akasicdb.cypher('retail_graph', $$
MATCH (c:v_customer)
WHERE c.first_name = $customer_name
RETURN
c.first_name as first_name,
c.last_name as last_name
$$, '{"customer_name":"Margaret"}') as (
first_name varchar,
last_name varchar
);
- 그래프 질의 내에서 파라미터는 $로 시작하는 심볼(위의
$customer_name)로 나타납니다. - 파라미터 값은 JSON 형식으로 함수에 전달할 수 있습니다.
-- 파라미터(customer_id, 값: 1)와 같은 `c_id`를 가지는 소비자의 풀네임을 조회
SELECT *
FROM akasicdb.cypher('retail_graph', $$
MATCH (c:v_customer)
WHERE c.c_id = $customer_id::integer
RETURN
c.first_name as first_name,
c.last_name as last_name
$$, '{"customer_id":1}') as (
first_name varchar,
last_name varchar
);
- 파라미터 값은 질의 내에서 기본적으로
TEXT타입으로 처리됩니다. - 정수형 등의 다른 타입을 써야 할 경우 질의 내에서 명시적으로 타입 캐스팅을 해야 합니다.
- SQL/GQL에서는 openCypher의
toInteger()와 같은 변환 함수 대신::integer와 같은 SQL 스타일 타입 캐스팅을 사용합니다. 단, 특수문자 등을 포함한 경우::'int[]'와 같이 타입 이름을 작은따옴표로 감싸서 사용해야 합니다.
SQL/GQL 사용 시 유의사항¶
AkasicDB의 그래프 질의 방언은 openCypher와 유사하지만 지원 범위와 의미 체계가 다릅니다. 이 문서에서 지원한다고 명시하지 않은 openCypher 문법은 사용할 수 있다고 가정하지 마세요. 반면, 문서에 나열되지 않은 PostgreSQL 함수도 PostgreSQL이 호환되는 타입을 해석할 수 있다면 동작할 수 있습니다. 이는 openCypher 호환성을 보장한다는 의미가 아닙니다. 현재 지원하지 않는 문법은 향후 업데이트에서 추가될 수 있습니다.
아래 예시를 사용하기 전에 다음 패턴 규칙을 확인해 주세요.
- 새로운 정점 변수가 그래프 패턴에 처음 등장할 때는 정확히 하나의 레이블을 지정해야 합니다. 같은 변수를 다시 사용할 때는 레이블을 생략할 수 있습니다.
c가 처음 등장할 때는(c:v_customer)처럼 작성해야 하며, 변수가 처음 등장할 때 레이블을 생략하거나 여러 레이블을 지정하는 패턴은 지원하지 않습니다. - 간선 패턴에는 하나의 간선 레이블을 지정해야 합니다.
(c:v_customer)-[r:buy]->(i:v_item)과 같은 형태를 사용해 주세요. -
모든 간선은 방향성이 있습니다. 탐색 시 정방향(
->) 또는 역방향(<-) 중 하나를 선택해야 하며,--와 같은 무방향 간선 패턴은 지원하지 않습니다. -
OPTIONAL MATCH는 현재 지원하지 않습니다. 다만 SQL의 outer join을 사용해서 SQL/GQL에서 유사한 동작의 질의를 작성할 수 있습니다. -
UNION등의 집합 연산은 현재akasicdb.cypher()내에서 지원하지 않습니다. 필요한 경우 SQL의 집합 연산을 통해 SQL/GQL에서 유사한 동작의 질의를 작성할 수 있습니다.openCypher SQL/GQL -
(v:vertex_label {key: "Value"})와 같은 property map은 지원하지 않습니다.WHERE절에 속성 관련 조건을 넣는 것으로 같은 동작의 질의를 작성할 수 있습니다.openCypher SQL/GQL -
exists()및 path variable은 현재 지원하지 않습니다. -
WITH와RETURN절에서는 정점, 간선 또는 경로를 그래프 객체로 반환할 수 없습니다. 정점 또는 간선 변수만 반환하면 여러 내부 레코드 컬럼으로 펼쳐질 수 있지만 그래프 객체가 생성되는 것은 아니므로, 필요한 스칼라 속성을 명시적으로 반환해 주세요. -
IN연산자는 현재akasicdb.cypher()내에서 지원하지 않습니다. -
openCypher의
collect()함수 대신 PostgreSQL의array_agg()함수를 사용하셔야 합니다.openCypher SQL/GQL -
PostgreSQL 배열을 사용하므로 배열 인덱스는 1부터 시작합니다.
-
List comprehension은 현재 지원하지 않습니다.
-
CALLsubquery는 현재 지원하지 않습니다. -
labels(n),type(r),properties(n)과 같은 openCypher 헬퍼 함수는 현재 지원하지 않습니다. 레이블은 패턴에 직접 지정하고, 속성은 이름으로 직접 접근해 주세요. -
openCypher의
id()함수는 현재 지원하지 않습니다. 정점은n.vertex_id, 간선은r.edge_id와 같은 내부 ID 속성을 직접 사용해 주세요. -
ORDER BY에서는 같은RETURN절에서 만든 alias를 참조할 수 없습니다. 그래프 질의 안에서는 원래 표현식 또는 속성으로 정렬하거나, 바깥 SQL 질의에서 반환 alias를 기준으로 정렬해 주세요. -
OFFSET은 그래프 질의 문법에 포함되지 않습니다.akasicdb.cypher()안에서는SKIP을 사용하거나 외부 SQL 질의에OFFSET을 적용해 주세요.LIMIT인자는BIGINT로 변환되며 현재 행의 변수를 참조할 수 없습니다. -
Cypher 데이터 변경 절인
CREATE,MERGE,SET,DELETE는 지원하지 않습니다. Graph DML에서 설명하는 SQL 함수와 SQLUPDATE를 사용해 주세요.