통합 데이터베이스에서의 그래프 쿼리: Cypher에서 포스팅 리스트까지

Posted on March 26, 2026
통합 데이터베이스에서의 그래프 쿼리: Cypher에서 포스팅 리스트까지

폴리글랏 아키텍처의 문제

현대 애플리케이션은 점점 더 관계를 탐색해야 합니다. 소셜 네트워크는 친구의 친구 쿼리가 필요하고, 사기 탐지 시스템은 거래 체인을 추적해야 하며, 추천 엔진은 구매와 선호도 그래프를 순회해야 합니다.

일반적인 해결책은 간단합니다: 관계형 데이터베이스 옆에 그래프 데이터베이스를 배포합니다. Neo4j나 Amazon Neptune이 그래프 워크로드를 처리하고, PostgreSQL이 나머지를 처리합니다.

이 방식은 두 시스템에 걸친 쿼리가 필요해질 때까지는 잘 작동합니다. 그 순간 근본적인 아키텍처 문제에 직면합니다:

  • 데이터 중복: 엔티티 데이터가 관계형 저장소와 그래프 저장소 양쪽에 존재해야 합니다.
  • 동기화: 한 시스템의 변경이 다른 시스템에 전파되어야 하며, 보통 Kafka 파이프라인이나 CDC 커넥터를 통해 이루어집니다.
  • 크로스 시스템 트랜잭션 부재: 관계형 INSERT와 그래프 MERGE를 아우르는 ACID 보장은 폴리글랏 아키텍처에서는 존재하지 않습니다.
  • 두 가지 쿼리 언어: 애플리케이션 코드가 SQL과 Cypher(또는 Gremlin) 사이를 오가야 하며, 이는 논리적으로는 하나의 질문인 경우가 많습니다.

Cognica는 이 경계를 완전히 제거하기 위해 설계되었습니다.

포스팅 리스트로서의 그래프 연산

Cognica의 엔진은 단일 수학적 추상화인 포스팅 리스트 위에 구축되어 있습니다. 모든 쿼리 패러다임 — 관계형 SQL, 텍스트 검색, 벡터 유사도, 그래프 순회 — 은 포스팅 리스트를 생성하고 불리언 대수로 합성하는 연산으로 컴파일됩니다.

그래프 쿼리에서 핵심적인 관찰은, 그래프 순회 결과가 문서 검색 결과와 구조적으로 동일하다는 것입니다: 둘 다 특정 술어를 만족하는 엔티티 ID의 집합입니다.

연산입력출력
WHERE age > 30테이블 스캔매칭 행 ID의 포스팅 리스트
WHERE content @@ 'database'역색인매칭 문서 ID의 포스팅 리스트
ORDER BY embedding <=> $vecHNSW 인덱스최근접 이웃 ID의 포스팅 리스트
MATCH (a)-[:FOLLOWS*1..3]->(b)인접 순회도달 가능 정점 ID의 포스팅 리스트

모든 연산이 포스팅 리스트를 반환하기 때문에, 동일한 불리언 연산자로 합성됩니다: AND(교집합), OR(합집합), NOT(여집합). 텍스트 검색과 그래프 순회를 결합하는 쿼리에 특별한 융합 로직이 필요하지 않습니다 — 단지 두 포스팅 리스트의 교집합일 뿐입니다.

문서 저장소 위의 속성 그래프

Cognica는 그래프를 문서 컬렉션 쌍으로 저장합니다. social이라는 그래프는 두 개의 컬렉션을 생성합니다:

social_nodes에 정점 저장:

{ "_id": "social:Person:1001", "_label": "Person", "_graph": "social", "name": "Alice", "age": 30, "department": "Engineering" }

social_edges에 간선 저장:

{ "_id": "social:FOLLOWS:2001", "_source": "social:Person:1001", "_target": "social:Person:1002", "_type": "FOLLOWS", "_graph": "social", "since": "2024-01-15" }

이 이중 컬렉션 모델의 핵심 장점은 문서 저장소 인프라 전체를 재사용한다는 것입니다. 인덱스, ACID 트랜잭션, LSM-Tree 컴팩션, 커서 순회, 페더레이션 — 이 모든 것이 그래프 전용 저장소 코드 한 줄 없이 그래프 데이터에서 작동합니다.

(_source, _type)(_target, _type)에 대한 복합 인덱스로 양방향 인접 조회를 효율적으로 수행합니다. LRU 인접 캐시는 사전 계산된 이웃 관계를 메모리에 유지하여 멀티홉 순회를 추가로 가속합니다.

쿼리 재작성을 통한 Cypher 지원

Cognica는 Apache AGE 호환 Cypher 구문을 지원합니다. 그러나 별도의 그래프 쿼리 엔진을 구축하는 대신, 파싱 시점에 Cypher를 SQL로 변환합니다.

SELECT * FROM cypher('social', $$ MATCH (a:Person)-[:FOLLOWS*1..3]->(b:Person) WHERE a.name = 'Alice' RETURN b.name, b.role $$) AS (name TEXT, role TEXT);

SQL Parser가 FROM 절에서 cypher() 함수를 만나면, 변환 파이프라인이 시작됩니다:

  1. Cypher Lexer — 패턴 화살표(-[:TYPE]->)와 대소문자 비구분 키워드를 토크나이즈하는 수작업 스캐너.
  2. Cypher Parser — 토큰 스트림에서 Cypher 전용 AST를 구축.
  3. Cypher-to-SQL Rewriter — Cypher AST를 그래프 테이블 함수를 참조하는 표준 SQL AST 노드로 변환.
  4. Query Optimizer — 재작성된 SQL이 다른 모든 쿼리에 사용되는 동일한 비용 기반 옵티마이저로 진입.

이 설계는 SQL 옵티마이저가 그래프 쿼리 구조에 대한 완전한 가시성을 가진다는 것을 의미합니다. 술어 푸시다운, 조인 재정렬, 인덱스 선택 모두 그래프-관계형 경계를 넘어 작동합니다. 최적화가 실행되는 시점에는 "그래프 연산"과 "관계형 연산"의 구분이 없기 때문입니다 — 모두 SQL 플랜 노드입니다.

하나의 쿼리로 패러다임 합성

진정한 힘은 그래프 연산이 다른 패러다임과 합성될 때 나타납니다. 텍스트 매칭, 벡터 유사도, 그래프 중심성, 관계형 필터링을 결합하는 논문 탐색 쿼리를 생각해 봅시다:

SELECT title, year, field, _score FROM papers WHERE fuse_log_odds( bayesian_match(title, 'attention'), knn_match(embedding, $1, 10), pagerank() ) AND year >= 2019 ORDER BY _score DESC LIMIT 5;

이 단일 SQL 문은:

  1. Bayesian BM25 확률적 스코어링으로 논문 제목을 검색
  2. HNSW 벡터 검색으로 의미적으로 유사한 논문을 탐색
  3. PageRank로 인용 그래프 중심성을 계산
  4. 로그 오즈(log-odds) 결합으로 세 신호를 융합
  5. 출판 연도로 필터링
  6. 상위 5개 결과를 반환

폴리글랏 아키텍처에서 이 쿼리는 세 개의 별도 시스템, 애플리케이션 레벨 점수 병합, 그리고 트랜잭션의 포기를 감수해야 합니다. Cognica에서는 하나의 쿼리, 하나의 트랜잭션, 하나의 옵티마이저입니다.

왜 이것이 중요한가

통합 접근 방식은 인프라의 한 계층 전체를 제거합니다:

  • 관계형 저장소와 그래프 저장소 간의 동기화 파이프라인이 불필요
  • 시스템 간 데이터 중복이 불필요
  • 쿼리 언어 간의 임피던스 불일치가 없음
  • 관계형과 그래프 연산을 아우르는 ACID 트랜잭션
  • 전체 쿼리 구조를 이해하는 단일 옵티마이저

그래프 쿼리는 덧붙여진 기능이 아닙니다. 포스팅 리스트 대수의 자연스러운 결과입니다 — 엔진의 다른 모든 패러다임과 마찬가지로, 불리언 로직으로 합성 가능한 ID 집합을 생성하는 또 하나의 연산입니다.

관계형 SQL, 텍스트 검색, 벡터 유사도와 함께 관계 인식 쿼리가 필요한 팀에게, 이는 쿼리 표현력이나 트랜잭션 보장에 타협 없이 서너 개의 시스템 대신 하나의 시스템을 배포하는 것을 의미합니다.

Copyright (c) 2023-2026 Cognica, Inc.