< back

Neo4j を VectorDB として使う

Neo4j は v5.13 で Vector search index を実装した。使い方は他の VectorDB と同様で、ベクタライズしたデータを Vector Index に格納する。Cypher クエリを通じて近似解を導出する。

この記事では、Vector Index の使い方について紹介する。具体例では、ノートアプリのバックエンドとして Neo4j を VectorDB として利用する想定で紹介する。"Note" ノードはそれぞれのノートの ID やメタデータを持つ。ノートのタイトルは "Title" ノードに保存する。今回の目玉となる Vector Index を "Abstract" ノードに担当させる。

CREATE
  (note:Note)-[:HAS]->(title:Title {text="Hello, world!"})
  , (note)-[:HAS]->(abstract:Abstract)
RETURN
  note, title, abstract

Neo4j Vector Search data-model

まずは下準備について。db.index.vector.createNodeIndex 命令を利用して、Vector Index を作成する。例えば、ノートアプリの要約を保存する "Abstract" インデックスを、ベクター数 768 で作成するコマンドは以下の通り。embedding プロパティにベクタライズしたデータを保存していく。Similarity Search のアルゴリズムとしては Euclidean と Cosine が現時点では使え、以下の例では Cosine Similarity を指定している。

CALL db.index.vector.createNodeIndex(
  'abstract-embeddings',
  'Abstract',
  'embedding',
  768,
  'cosine'
)

次は書き込みについて。db.create.setNodeVectorProperty 命令を利用して、ベクターデータを格納する。これで読み込みの準備は完了だ。

MATCH
  (abstract:Abstract)<-[:HAS]-(note:Note)-[:HAS]->(title:Title{text="Hello, world!"})
CALL
  db.create.setNodeVectorProperty(
    abstract,
    'embedding',
    [0.312, 0.234, -0.456, ...]
  )
RETURN
  abstract, note, title

ベクターデータをすべての Vector Index に格納したら、最後は読み込みだ。db.index.vector.queryNodes 命令を利用して、Vector Index から近似スコアを導出する。例えば以下の例では、Hello world! I'm LLM app! というタイトルを持つ "Note" ノードに近似した別の "Note" ノードを、Vector Index である "Abstract" ノードのスコア上位三件を取得している。

MATCH (title:Title)<--(:Note)-->(abstract:Abstract)
WHERE title.text = "Hello world! I'm LLM app!"

CALL db.index.vector.queryNodes(
  'abstract-embeddings',
  3,
  abstract.embedding
)
YIELD node AS similarAbstract, score

MATCH (similarAbstract)<--(:Note)-->(similarTitle:Title)
RETURN similarTitle.text AS title, score

以下がクエリ結果の例だ。"Hello world!" を含んだノートが上位スコアで返却されていることがわかる。これは "title" プロパティに格納されたベクターデータの Cosine Similarity を計算し、最もベクトル座標上近い上位三件を取得した結果である。

╒══════════════════════════════════════════════════════════════════╤══════════════════╕
│title                                                             │score             │
╞══════════════════════════════════════════════════════════════════╪══════════════════╡
│"Hello world! I'm LLM app!"                                       │1.0               │
├──────────────────────────────────────────────────────────────────┼──────────────────┤
│"Hello world! Here is a RAG demo."                                │0.8671051263809204│
├──────────────────────────────────────────────────────────────────┼──────────────────┤
│"Here is a RAG demo. I'm going to show you how to create the app."│0.8667137622833252│
└──────────────────────────────────────────────────────────────────┴──────────────────┘
December 03, 2023

Recommended

  1. Semantic Search をブログに実装した

    Semantic Search をブログに実装した。VectorDB (Pinecone) の実験としての意味合いが強い。 技術構成を紹介する。まずはインデックスの更新フローについて。Markdown ファイルで入稿したブログ記事から、Gatsby でビルド時にメタデータを抽出する。メタデータとしては、記事の URL…
  2. Neo4j Causal Clustering 紹介

    本記事では、Neo4j における Causal Clustering (因果クラスタリング) について説明します。 クラスタリングの目的 データベースを運用する場合、単一のインスタンスを稼働させるか、Causal Clustering を用いてクラスター構成を組むか(クラスタリング)のいずれかを選択することになるでし…
  3. Neo4j Bolt Handshake Protocol Introduced

    Bolt Protocol において、サーバーとクライアントが接続を開始するためのプロトコルとして、Bolt Handshake Protocol Specification の仕様が策定されています。 本記事では、Bolt Handshake Protocol の概要について説明します。 Handshake Wor…