はじめの一歩

この章では、Neo4j を使ってグラフDB を実際に体験します。環境構築から Cypher の基本操作まで、RDB 経験者が迷わず進められるように SQL との対比を交えて解説します。

環境の準備

Neo4j を試す方法は3つあります。目的に応じて選んでください。

方法特徴推奨シーン
Neo4j DesktopGUI アプリ。ローカルで完結じっくり学習したい(要アカウント登録)
Neo4j Aura Freeクラウド。インストール不要すぐに試したい(要アカウント登録)
Docker / Podmanコマンド1つで起動。アカウント不要開発環境に慣れている

Neo4j Desktop で始める

  1. neo4j.com/download から Neo4j Desktop をダウンロード(無料、要アカウント登録)
  2. インストーラーを実行し、起動
  3. 「New Project」→「Add Database」→「Local DBMS」を選択
  4. パスワードを設定して「Create」
  5. 「Start」でデータベースを起動
  6. 「Open」で Neo4j Browser が起動し、Cypher を入力できる画面が開く

Neo4j Browser はグラフを視覚的に表示できる組み込みツールです。ノードとエッジが図として表示されるので、データの関係を直感的に理解できます。RDB の管理ツール(pgAdmin や SQL Developer)に相当します。

Neo4j Aura で始める

  1. neo4j.com/cloud/aura-free にアクセス
  2. アカウントを作成し、「Create Free Instance」をクリック
  3. 生成された接続情報(パスワード)を控える
  4. 数分でインスタンスが起動し、ブラウザから Cypher を実行可能

Aura Free は容量制限(ノード数 20万、関係数 40万)がありますが、学習には十分です。

Docker(又は Podman)で始める

以下は Docker のコマンド例です。Podman でも dockerpodman に読み替えればそのまま動作します。

# 永続化先のディレクトリを作成
mkdir -p ~/neo4j/{data,logs,conf}

# Neo4j を起動
# ポート 7474: Browser, 7687: Bolt
docker run -d \
  --name neo4j \
  -p 7474:7474 -p 7687:7687 \
  -v ~/neo4j/data:/data \
  -v ~/neo4j/logs:/logs \
  -v ~/neo4j/conf:/conf \
  -e NEO4J_AUTH=neo4j/password123 \
  neo4j:latest

起動後、ブラウザで http://localhost:7474 にアクセスすると Neo4j Browser が開きます。

ボリュームの役割: -v ~/neo4j/data:/data でホスト側のディレクトリをマウントしているため、コンテナを停止・削除してもデータはそのまま残ります。/logs/conf も永続化しておくと、ログの確認や設定変更がコンテナ再作成後も引き継がれます。

Cypher の基本

Cypher は Neo4j のクエリ言語で、SQL に似た宣言型の構文を持ちます。最大の特徴はグラフパターンを ASCII アートのように記述できることです。

基本構文の対比

操作SQLCypher
データ作成INSERT INTOCREATE
データ検索SELECT ... FROM ... WHEREMATCH ... WHERE ... RETURN
データ更新UPDATE ... SETMATCH ... SET
データ削除DELETE FROMMATCH ... DELETE
条件指定WHEREWHERE(同じ)
並べ替えORDER BYORDER BY(同じ)
件数制限LIMITLIMIT(同じ)

パターン記法

Cypher の核心はパターン記法です。ノードを丸括弧 ()、エッジを角括弧と矢印 -[]-> で表現します。

// ノード
(p:Person {name: "Alice"})

// エッジ(方向あり)
(a)-[:KNOWS]->(b)

// パターンの組み合わせ
(a:Person)-[:WORKS_AT]->(c:Company {name: "Acme"})

データの作成

以下のサンプルデータを作成して、Cypher の基本操作を体験しましょう。「会社の従業員と部署」という RDB 経験者に馴染みのある題材を使います。

// 部署を作成
CREATE (dev:Department {name: "開発部", floor: 3})
CREATE (sales:Department {name: "営業部", floor: 5})

// 従業員を作成
CREATE (tanaka:Employee {name: "田中", age: 35, role: "リーダー"})
CREATE (suzuki:Employee {name: "鈴木", age: 28, role: "メンバー"})
CREATE (sato:Employee {name: "佐藤", age: 42, role: "部長"})
CREATE (yamada:Employee {name: "山田", age: 31, role: "メンバー"})

// 関係を作成
CREATE (tanaka)-[:BELONGS_TO {since: 2020}]->(dev)
CREATE (suzuki)-[:BELONGS_TO {since: 2023}]->(dev)
CREATE (sato)-[:BELONGS_TO {since: 2015}]->(dev)
CREATE (yamada)-[:BELONGS_TO {since: 2021}]->(sales)

// 上司-部下の関係
CREATE (tanaka)-[:REPORTS_TO]->(sato)
CREATE (suzuki)-[:REPORTS_TO]->(tanaka)

// 同僚関係(プロジェクトでの協業)
CREATE (tanaka)-[:COLLABORATES {project: "システムA"}]->(yamada)

SQL との違い: RDB ではまず CREATE TABLE でスキーマを定義し、その後 INSERT でデータを投入します。Cypher ではスキーマ定義なしにいきなりデータを作成できます。ラベル(:Employee)とプロパティ({name: "田中"})はデータ投入時に決まります。

データの検索

基本的な検索

// 全従業員を取得(SQL: SELECT * FROM employees)
MATCH (e:Employee)
RETURN e.name, e.age, e.role

// 条件付き検索(SQL: SELECT * FROM employees WHERE age > 30)
MATCH (e:Employee)
WHERE e.age > 30
RETURN e.name, e.age

関係をたどる検索

// 開発部に所属する従業員(SQL: SELECT ... FROM employees JOIN departments ...)
MATCH (e:Employee)-[:BELONGS_TO]->(d:Department {name: "開発部"})
RETURN e.name, e.role

// 田中の上司を探す
MATCH (e:Employee {name: "田中"})-[:REPORTS_TO]->(boss)
RETURN boss.name, boss.role

// 佐藤の部下を全て探す(直属 + 間接)
MATCH (boss:Employee {name: "佐藤"})<-[:REPORTS_TO*1..3]-(sub)
RETURN sub.name

集計

// 部署ごとの人数(SQL: SELECT dept, COUNT(*) GROUP BY dept)
MATCH (e:Employee)-[:BELONGS_TO]->(d:Department)
RETURN d.name, count(e) AS members
ORDER BY members DESC

データの更新

// プロパティの更新(SQL: UPDATE employees SET age = 36 WHERE name = '田中')
MATCH (e:Employee {name: "田中"})
SET e.age = 36

// プロパティの追加(スキーマ変更不要!)
MATCH (e:Employee {name: "田中"})
SET e.email = "[email protected]"

// ラベルの追加(RDB にはない概念)
MATCH (e:Employee {name: "佐藤"})
SET e:Manager

// 新しい関係の追加
MATCH (a:Employee {name: "鈴木"}), (b:Employee {name: "山田"})
CREATE (a)-[:COLLABORATES {project: "システムB"}]->(b)

ALTER TABLE が不要: RDB で列を追加するには ALTER TABLE ADD COLUMN が必要ですが、Cypher では SET で新しいプロパティをいきなり追加できます。既存のノードに影響はありません。

データの削除

// 関係の削除
MATCH (e:Employee {name: "田中"})-[r:COLLABORATES]->(other)
DELETE r

// ノードの削除(関係があるノードは先に関係を削除する必要がある)
MATCH (e:Employee {name: "山田"})-[r]-()
DELETE r, e

// DETACH DELETE で関係ごとまとめて削除
MATCH (e:Employee {name: "山田"})
DETACH DELETE e

注意: RDB の ON DELETE CASCADE のような自動連鎖削除はありません。関係を持つノードを削除するには DETACH DELETE を使うか、先に関係を削除する必要があります。

RDB 概念との対応表

RDB の知識をグラフDB に活かすための対応表です。

RDB の概念グラフDB の対応概念備考
テーブルラベル:Employee, :Department
行(レコード)ノードプロパティの集合
列(カラム)プロパティスキーマレスで柔軟
主キー内部ID(自動)+ ユニーク制約CREATE CONSTRAINT で設定
外部キーエッジ直接参照。JOIN 不要
中間テーブルエッジ(プロパティ付き)多対多も自然に表現
JOINパターンマッチMATCH (a)-[r]->(b)
再帰CTE可変長パス[:REL*1..5]
CREATE TABLE不要データ作成時に暗黙的に定義
ALTER TABLE不要SET で随時追加
INDEXCREATE INDEXプロパティにインデックスを設定可能
UNIQUE 制約CREATE CONSTRAINT ... IS UNIQUENeo4j でサポート

ラベル名・エッジタイプ名のリネームはできない: 「CREATE TABLE が不要」と聞くと自由度が高く感じますが、一度付けたラベル名(例: :Employee)やエッジタイプ名(例: :BELONGS_TO)を一括リネームする機能は Neo4j には存在しません。変更するには全対象ノードに対して「新ラベルを付与 → 旧ラベルを削除」を自分で実行する必要があります。

// ラベルを Employee → Staff に変更する場合
MATCH (n:Employee)
SET n:Staff
REMOVE n:Employee

RDB の ALTER TABLE RENAME に相当する操作が無いため、ラベル名やエッジタイプ名は最初の設計時に慎重に決めましょう。スキーマレスだからといって「後から何でも変えられる」わけではない点は、RDB 経験者が特に注意すべきポイントです。

練習問題

上記のサンプルデータを使って、以下のクエリを Cypher で書いてみましょう。

  1. 30歳以上の従業員の名前と年齢を取得する
  2. 開発部に所属する従業員の名前を取得する
  3. 田中が協業しているプロジェクト名と相手の名前を取得する
  4. 佐藤から2ホップ以内の部下全員の名前を取得する
  5. 全従業員に company プロパティとして "ACME" を追加する

解答例

// 1.
MATCH (e:Employee) WHERE e.age >= 30 RETURN e.name, e.age

// 2.
MATCH (e:Employee)-[:BELONGS_TO]->(d:Department {name:"開発部"}) RETURN e.name

// 3.
MATCH (e:Employee {name:"田中"})-[c:COLLABORATES]->(other)
RETURN c.project, other.name

// 4.
MATCH (boss:Employee {name:"佐藤"})<-[:REPORTS_TO*1..2]-(sub)
RETURN sub.name

// 5.
MATCH (e:Employee) SET e.company = "ACME"

Java からのアクセス

RDB に JDBC ドライバがあるように、Neo4j にも公式の Java ドライバがあります。 接続には Bolt プロトコル(ポート 7687)を使います。JDBC Type 4 ドライバと同様に純 Java 実装で、ネイティブライブラリは不要です。

ドライバの比較

観点JDBC(RDB)Neo4j Java Driver
接続文字列jdbc:postgresql://host:5432/dbbolt://host:7687
クエリ言語SQLCypher
結果の型ResultSetResult / Record
トランザクションConnection.setAutoCommit()Session.executeWrite()
コネクション管理DataSource / コネクションプールDriver(内蔵プール)

Maven 依存

<dependency>
  <groupId>org.neo4j.driver</groupId>
  <artifactId>neo4j-java-driver</artifactId>
  <version>5.27.0</version>
</dependency>

コード例: 接続・作成・検索

import org.neo4j.driver.*;

public class Neo4jExample {
    public static void main(String[] args) {
        // ドライバ生成(内蔵コネクションプール付き)
        // RDB の DataSource に相当
        try (Driver driver = GraphDatabase.driver(
                "bolt://localhost:7687",
                AuthTokens.basic("neo4j", "password123"))) {

            // セッション ≒ JDBC の Connection
            try (Session session = driver.session()) {

                // 書き込みトランザクション(ノードと関係を作成)
                session.executeWrite(tx -> {
                    tx.run("""
                        MERGE (a:Person {name: $name1})
                        MERGE (b:Person {name: $name2})
                        MERGE (a)-[:KNOWS {since: $since}]->(b)
                        """,
                        Values.parameters(
                            "name1", "Alice",
                            "name2", "Bob",
                            "since", 2024));
                    return null;
                });

                // 読み取りトランザクション(検索)
                session.executeRead(tx -> {
                    Result result = tx.run("""
                        MATCH (a:Person {name: $name})-[:KNOWS]->(friend)
                        RETURN friend.name AS friendName
                        """,
                        Values.parameters("name", "Alice"));

                    // RDB の ResultSet.next() に相当
                    while (result.hasNext()) {
                        Record record = result.next();
                        System.out.println(record.get("friendName").asString());
                    }
                    return null;
                });
            }
        }
    }
}

JDBC との類似点: DriverSessionTransactionResult という階層は、JDBC の DataSourceConnectionStatementResultSet とほぼ対応しています。また、$name はJDBC のプレースホルダ ? に相当するパラメータで、SQL インジェクションと同様のインジェクション対策として必ずパラメータ化して使います。

その他の言語: Neo4j は Java 以外にも Python(neo4j パッケージ)、JavaScript/TypeScript(neo4j-driver)、.NET、Go の公式ドライバを提供しています。いずれも Bolt プロトコルで接続し、API の構造は Java 版と同様です。

次のステップ

このガイドで基礎を学んだら、以下のリソースでさらに理解を深められます。

RDB 経験者へのアドバイス: グラフDB は RDB を否定するものではなく、補完するものです。まずは「RDB で書くと再帰CTE や多段 JOIN になって辛い」と感じた場面を思い出してください。そこがグラフDB の出番です。既存の RDB はそのまま活かしつつ、関係探索の部分だけグラフDB を導入するのが最も堅実なアプローチです。