Graph Query

AkasicDB에서 akaiscdb.cypher() 함수를 통해 그래프 질의를 수행하는 방법을 안내합니다.


1. 그래프 준비

테이블 준비는 링크를 참고해주세요.

-- 그래프 'retail_graph'를 정의
SELECT akasicdb.define_graph('retail_graph');

-- 그래프 정의 'retail_graph'에 정점 정의 'v_item', 'v_customer', 'v_store' 추가
SELECT akasicdb.define_vertex('retail_graph', 'v_item', 'item');
SELECT akasicdb.define_vertex('retail_graph', 'v_customer', 'customer');
SELECT akasicdb.define_vertex('retail_graph', 'v_store', 'store');

-- 그래프 정의 'retail_graph'에 간선 정의 'buy', 'sell' 추가
SELECT akasicdb.define_edge('retail_graph', 'sell',
                            'v_store', 'store s',
                            'v_item', 'item i',
                            'SELECT null FROM store s, orders o, item i '
                            'WHERE s.s_id = o.s_id AND i.i_no = o.i_no');
SELECT akasicdb.define_edge('retail_graph', 'buy',
                            'v_customer', 'customer c',
                            'v_item', 'item i',
                            'SELECT null FROM customer c, orders o, item i '
                            'WHERE c.c_id = o.c_id AND i.i_no = o.i_no');

-- 그래프 정의 'retail_graph'에 간선 정의 'co_purchase' 추가
SELECT akasicdb.define_edge('test', 'co_purchase',
                           'v_customer', 'customer c1',
                           'v_customer', 'customer c2',
                           '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');

-- 그래프 정의 `retail_graph`를 기반으로 그래프 생성                   
SELECT akasicdb.create_graph('retail_graph');

2. Cypher 질의 인터페이스

함수 인자 설명

akasicdb.cypher(
    graph_name   NAME, -- 질의를 수행할 그래프 이름
    query_string TEXT  -- 수행할 cypher 질의
);

그래프 질의는 cypher 질의의 형태를 띄어야 합니다. 이때, query_string의 경우 $$(dollar quote)로 둘러싸인 문자열이어야 합니다. 또한, akasicdb.cypher() 호출 뒤에는 AS 절을 붙여서, cypher 질의의 결과를 SQL 질의에서 튜플의 형태로 다룰 수 있도록 해야 합니다.

2.1 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('test', $$
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
);

2.2 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 char 
); 

2.3. 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.vertex_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 char,
	cnt integer
) 

2.4. VLE(Variable Length Edge) 패턴

-- co_purchase 간선을 통해 비슷한 구매 이력을 가진 소비자의 네트워크를 구축했을 때,
-- Latisha Hamilton으로부터 거리 1~3 이내의 소비자를 조회
SELECT *
FROM akasicdb.cypher('test', $$
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(Variable Length Edge)와 TC(Transitive Closure) 패턴의 경우, 아래의 제약을 가지고 있습니다.
    • 시작/끝 정점 레이블이 동일한 단일 간선 레이블만 사용할 수 있습니다.
    • 탐색을 시작하는 정점(위 경우 c1)이 필터링되지 않을 경우 굉장히 느릴 수 있습니다.
    • 탐색할 hop 수가 클 경우 굉장히 느릴 수 있습니다. 단, SHORTEST가 포함될 경우 hop 수가 크더라도 좋은 성능이 유지될 수 있습니다.

2.5. Cypher 내에서 파라미터 사용하기

-- 파라미터(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
);
  • Cypher 질의 내에서 파라미터는 $로 시작하는 심볼(위의 $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 타입으로 처리됩니다.
  • 정수형 등의 다른 타입을 써야 할 경우 질의 내에서 명시적으로 타입 캐스팅을 해야 합니다.

This site uses Just the Docs, a documentation theme for Jekyll.