Neo4j 벡터 인덱스

그래프 DB 안에서 벡터 유사도 검색 구현하기

Neo4j에 벡터 인덱스를 생성하여 그래프 탐색과 의미적 유사도 검색을 하나의 DB에서 수행한다. 노드에 임베딩 속성을 저장하고, Neo4jVector로 LangChain에서 활용하며, neo4j-graphrag-python의 VectorRetriever를 사용하는 방법을 다룬다.

AI
RAG
GraphRAG
Neo4j
저자

Kwangmin Kim

공개

2026년 03월 08일

1 Neo4j 벡터 인덱스

1.1 Neo4j에서 벡터 검색이 가능한 이유

Neo4j 5.11부터 벡터 인덱스(Vector Index)를 지원한다. 노드의 속성에 임베딩 벡터를 저장하고, 코사인/유클리드 유사도 검색이 가능하다.

[기존: 벡터 DB 별도]          [Neo4j 통합]
  텍스트 → 임베딩 → Chroma    텍스트 → 임베딩 → Neo4j 노드 속성
  그래프 → Neo4j               그래프 → Neo4j 관계
  → 두 DB 동기화 필요           → 하나의 DB에서 모든 처리

1.2 벡터 인덱스 생성

-- Cypher로 직접 생성
CREATE VECTOR INDEX document_embeddings IF NOT EXISTS
FOR (n:Document)
ON n.embedding
OPTIONS {
  indexConfig: {
    `vector.dimensions`: 1536,         -- text-embedding-3-small 기준
    `vector.similarity_function`: 'cosine'
  }
}
# Python에서 생성
from langchain_neo4j import Neo4jGraph

graph = Neo4jGraph(url="bolt://localhost:7687",
                   username="neo4j", password="password")

graph.query("""
CREATE VECTOR INDEX document_embeddings IF NOT EXISTS
FOR (n:Document) ON n.embedding
OPTIONS {
  indexConfig: {
    `vector.dimensions`: 1536,
    `vector.similarity_function`: 'cosine'
  }
}
""")

# 인덱스 목록 확인
graph.query("SHOW VECTOR INDEXES")

1.3 임베딩 저장

1.3.1 방법 1: Neo4jVector.from_documents (LangChain)

문서를 임베딩하여 Neo4j에 저장하는 가장 간단한 방법.

from langchain_neo4j import Neo4jVector
from langchain_openai import OpenAIEmbeddings
from langchain_core.documents import Document

documents = [
    Document(
        page_content="Tesla was founded by Elon Musk in 2003.",
        metadata={"source": "news", "date": "2023-01-01"},
    ),
    Document(
        page_content="SpaceX develops reusable rockets for space exploration.",
        metadata={"source": "wiki"},
    ),
]

vector_store = Neo4jVector.from_documents(
    documents=documents,
    embedding=OpenAIEmbeddings(model="text-embedding-3-small"),
    url="bolt://localhost:7687",
    username="neo4j",
    password="password",
    index_name="document_embeddings",
    node_label="Document",               # 생성할 노드 레이블
    text_node_property="text",           # 텍스트를 저장할 속성명
    embedding_node_property="embedding", # 임베딩을 저장할 속성명
)

1.3.2 방법 2: 기존 그래프 노드에 임베딩 추가

LLMGraphTransformer로 구축한 KG 노드에 임베딩을 추가한다.

from langchain_openai import OpenAIEmbeddings
import numpy as np

embedder = OpenAIEmbeddings(model="text-embedding-3-small")
graph = Neo4jGraph(...)

# 임베딩 없는 노드 조회
nodes = graph.query("""
MATCH (n:__Entity__)
WHERE n.embedding IS NULL
RETURN n.id AS id, n.description AS text
LIMIT 100
""")

# 배치 임베딩
texts = [n.get("text") or n["id"] for n in nodes]
embeddings = embedder.embed_documents(texts)

# Neo4j에 저장
for node, embedding in zip(nodes, embeddings):
    graph.query("""
    MATCH (n:__Entity__ {id: $id})
    SET n.embedding = $embedding
    """, params={"id": node["id"], "embedding": embedding})

print(f"{len(nodes)}개 노드 임베딩 완료")

1.4 벡터 유사도 검색

1.4.1 Cypher로 직접 검색

from langchain_openai import OpenAIEmbeddings

embedder = OpenAIEmbeddings(model="text-embedding-3-small")
query = "Who founded Tesla?"
query_embedding = embedder.embed_query(query)

results = graph.query("""
CALL db.index.vector.queryNodes(
    'document_embeddings',  -- 인덱스 이름
    5,                       -- top-k
    $embedding               -- 질의 벡터
)
YIELD node, score
RETURN node.text AS text, node.id AS id, score
ORDER BY score DESC
""", params={"embedding": query_embedding})

for r in results:
    print(f"score={r['score']:.3f} | {r['text'][:100]}")

1.4.2 Neo4jVector.similarity_search (LangChain)

# similarity_search: 텍스트 입력 → 자동 임베딩 → 검색
similar_docs = vector_store.similarity_search(
    "Who founded Tesla?",
    k=5,
)
for doc in similar_docs:
    print(doc.page_content)

# similarity_search_with_score: 유사도 점수 포함
docs_with_scores = vector_store.similarity_search_with_score(
    "Tesla founding",
    k=5,
)
for doc, score in docs_with_scores:
    print(f"score={score:.3f} | {doc.page_content}")

1.5 기존 Neo4j 그래프와 통합

LLMGraphTransformer로 구축한 KG와 벡터 인덱스를 연결한다.

# 이미 KG가 구축된 Neo4j에 벡터 인덱스 추가
vector_store = Neo4jVector.from_existing_graph(
    embedding=OpenAIEmbeddings(model="text-embedding-3-small"),
    url="bolt://localhost:7687",
    username="neo4j",
    password="password",
    index_name="entity_embeddings",
    node_label="__Entity__",        # LLMGraphTransformer가 생성한 레이블
    text_node_properties=["id", "description"],  # 임베딩할 텍스트 속성
    embedding_node_property="embedding",
)

1.6 neo4j-graphrag의 VectorRetriever

공식 Neo4j GraphRAG 패키지의 검색기.

import neo4j
from neo4j_graphrag.retrievers import VectorRetriever
from neo4j_graphrag.embeddings import OpenAIEmbeddings

driver = neo4j.GraphDatabase.driver(
    "bolt://localhost:7687", auth=("neo4j", "password")
)
embedder = OpenAIEmbeddings(model="text-embedding-3-small")

retriever = VectorRetriever(
    driver=driver,
    index_name="document_embeddings",
    embedder=embedder,
    return_properties=["text", "source"],  # 반환할 속성
)

results = retriever.search(
    query_text="Who founded Tesla?",
    top_k=5,
)

for item in results.items:
    print(f"score={item.score:.3f} | {item.content}")

1.7 벡터 인덱스 관리

-- 인덱스 목록
SHOW VECTOR INDEXES

-- 인덱스 삭제
DROP INDEX document_embeddings IF EXISTS

-- 인덱스 상태 확인 (ONLINE이어야 검색 가능)
SHOW INDEXES WHERE type = 'VECTOR'

1.8 정리

벡터 인덱스 생성:
  CREATE VECTOR INDEX idx_name
  FOR (n:Label) ON n.embedding
  OPTIONS { indexConfig: { `vector.dimensions`: 1536, ... } }

임베딩 저장:
  Neo4jVector.from_documents()      ← 신규 문서
  Neo4jVector.from_existing_graph() ← 기존 KG 노드

벡터 검색:
  Cypher: CALL db.index.vector.queryNodes(...)
  LangChain: vector_store.similarity_search()
  공식 패키지: VectorRetriever.search()

장점: 그래프 탐색 + 벡터 검색을 하나의 DB에서 처리

다음 파일에서는 벡터 검색과 Cypher 탐색을 결합한 하이브리드 검색을 구현한다.

Subscribe

Enjoy this blog? Get notified of new posts by email: