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 탐색을 결합한 하이브리드 검색을 구현한다.