データベース

Neo4j

概要

Neo4jは、世界をリードするグラフデータベース管理システムです。データをノード(要素)とエッジ(関係性)で表現し、複雑な関係性を持つデータの格納、クエリ、分析に最適化されています。宣言的なクエリ言語「Cypher」を使用し、自然言語に近い直感的な構文でグラフパターンをクエリできます。

詳細

Neo4jは2007年にNeo Technology(現Neo4j Inc.)によって開発されました。従来のリレーショナルデータベースでは複雑になりがちな関係性データを、ネイティブなグラフ構造で効率的に処理できます。ソーシャルネットワーク、推薦システム、不正検知、ナレッジグラフなど、関係性が重要なアプリケーションで広く使用されています。

Neo4jの主な特徴:

  • ネイティブグラフストレージとエンジン
  • ACID特性の完全サポート
  • 宣言的クエリ言語Cypher
  • インデックスとクエリ最適化
  • 高可用性クラスタリング
  • 豊富な可視化ツール
  • プログラミング言語の広範囲サポート
  • リアルタイムトランザクション処理
  • スケーラブルアーキテクチャ
  • 暗号化とアクセス制御

メリット・デメリット

メリット

  • 直感的なモデリング: グラフ構造による自然なデータ表現
  • 高速な関係性クエリ: JOIN不要でパフォーマンスが優秀
  • 柔軟性: スキーマフリーで進化するデータモデルに対応
  • 可視化: グラフの直感的な視覚化とブラウジング
  • Cypherクエリ: SQLライクで学習しやすい言語
  • エコシステム: 豊富なツールとライブラリ
  • リアルタイム: 即座のグラフトラバーサル

デメリット

  • 学習コスト: グラフデータベースの概念習得が必要
  • メモリ使用量: 大規模グラフで多くのメモリを消費
  • 特化型: 表形式データには非効率的
  • 複雑な集計: 統計処理がリレーショナルDBより複雑
  • ツール制限: BI ツールとの統合が限定的

主要リンク

書き方の例

インストール・セットアップ

# Docker での実行
docker run --name neo4j-container \
  -p 7474:7474 -p 7687:7687 \
  -e NEO4J_AUTH=neo4j/password \
  -v neo4j-data:/data \
  neo4j:latest

# デスクトップ版インストール
# https://neo4j.com/download/ からダウンロード

# Neo4j Browser アクセス
# http://localhost:7474

# Python ドライバーインストール
pip install neo4j

# Node.js ドライバーインストール
npm install neo4j-driver

基本操作(CRUD)

// ノード作成(Create)
CREATE (p:Person {name: '田中太郎', age: 30, email: '[email protected]'})
CREATE (c:Company {name: 'テック株式会社', industry: 'IT'})

// 関係性作成
CREATE (p)-[:WORKS_FOR {since: 2020, position: 'エンジニア'}]->(c)

// 複数ノードの作成
CREATE 
  (alice:Person {name: 'アリス', age: 25}),
  (bob:Person {name: 'ボブ', age: 28}),
  (alice)-[:FRIENDS_WITH {since: '2019-01-15'}]->(bob)

// ノード読み取り(Read)
MATCH (p:Person) RETURN p

// 特定条件でのクエリ
MATCH (p:Person {name: '田中太郎'}) RETURN p

// 関係性を含むクエリ
MATCH (p:Person)-[r:WORKS_FOR]->(c:Company)
RETURN p.name, r.position, c.name

// ノード更新(Update)
MATCH (p:Person {name: '田中太郎'})
SET p.age = 31, p.location = '東京'
RETURN p

// 関係性更新
MATCH (p:Person {name: '田中太郎'})-[r:WORKS_FOR]->(c:Company)
SET r.position = 'シニアエンジニア'

// ノード削除(Delete)
MATCH (p:Person {name: '田中太郎'})
DELETE p

// 関係性も含めて削除
MATCH (p:Person {name: '田中太郎'})
DETACH DELETE p

データモデリング

// ユーザーと商品の関係モデル
CREATE 
  (user:User {id: 1, name: '佐藤花子', email: '[email protected]'}),
  (product1:Product {id: 101, name: 'ノートPC', price: 80000}),
  (product2:Product {id: 102, name: 'マウス', price: 2000}),
  (category:Category {name: 'コンピュータ'}),
  (user)-[:PURCHASED {date: '2024-01-15', quantity: 1}]->(product1),
  (user)-[:VIEWED {timestamp: datetime()}]->(product2),
  (product1)-[:BELONGS_TO]->(category),
  (product2)-[:BELONGS_TO]->(category)

// ソーシャルネットワークモデル
CREATE 
  (alice:Person {name: 'アリス', age: 25, city: '東京'}),
  (bob:Person {name: 'ボブ', age: 28, city: '大阪'}),
  (charlie:Person {name: 'チャーリー', age: 30, city: '東京'}),
  (alice)-[:FOLLOWS]->(bob),
  (bob)-[:FOLLOWS]->(charlie),
  (charlie)-[:FOLLOWS]->(alice),
  (alice)-[:FRIENDS_WITH]->(charlie)

インデックス・最適化

// インデックス作成
CREATE INDEX person_name_index FOR (p:Person) ON (p.name)
CREATE INDEX product_id_index FOR (p:Product) ON (p.id)

// 複合インデックス
CREATE INDEX person_name_age_index FOR (p:Person) ON (p.name, p.age)

// 制約作成
CREATE CONSTRAINT person_email_unique FOR (p:Person) REQUIRE p.email IS UNIQUE
CREATE CONSTRAINT product_id_exists FOR (p:Product) REQUIRE p.id IS NOT NULL

// インデックス確認
SHOW INDEXES

// 制約確認
SHOW CONSTRAINTS

// クエリプラン確認
EXPLAIN MATCH (p:Person {name: '田中太郎'}) RETURN p
PROFILE MATCH (p:Person)-[:WORKS_FOR]->(c:Company) RETURN p, c

// データベース統計
CALL db.stats.retrieve()

実用例

// パス検索(最短経路)
MATCH path = shortestPath(
  (start:Person {name: 'アリス'})-[*]-(end:Person {name: 'チャーリー'})
)
RETURN path, length(path)

// 推薦システム(協調フィルタリング)
MATCH (user:User {name: '佐藤花子'})-[:PURCHASED]->(product:Product)<-[:PURCHASED]-(otherUser:User)
MATCH (otherUser)-[:PURCHASED]->(recommendation:Product)
WHERE NOT (user)-[:PURCHASED]->(recommendation)
RETURN recommendation.name, count(*) as score
ORDER BY score DESC
LIMIT 5

// 不正検知(異常パターン検出)
MATCH (account:Account)-[t:TRANSFER]->(suspicious:Account)
WHERE t.amount > 100000 
  AND t.timestamp > datetime() - duration('P1D')
WITH suspicious, count(t) as transfer_count
WHERE transfer_count > 10
RETURN suspicious

// ソーシャルネットワーク分析
MATCH (person:Person)-[:FRIENDS_WITH*2]-(friendOfFriend:Person)
WHERE person.name = 'アリス' AND person <> friendOfFriend
RETURN DISTINCT friendOfFriend.name as suggestions

// 影響力分析(中心性)
MATCH (p:Person)-[:FOLLOWS]->(other:Person)
RETURN p.name, count(other) as followers
ORDER BY followers DESC
LIMIT 10

ベストプラクティス

// トランザクション管理
BEGIN
CREATE (p:Person {name: '新規ユーザー', email: '[email protected]'})
CREATE (p)-[:WORKS_FOR]->(c:Company {name: '新会社'})
COMMIT

// バッチ処理(LOAD CSV)
LOAD CSV WITH HEADERS FROM 'file:///users.csv' AS row
CREATE (p:Person {
  id: toInteger(row.id),
  name: row.name,
  email: row.email,
  age: toInteger(row.age)
})

// パフォーマンス最適化
// MERGE の使用(重複回避)
MERGE (p:Person {email: '[email protected]'})
ON CREATE SET p.created = datetime()
ON MATCH SET p.lastSeen = datetime()

// WITH句による結果の絞り込み
MATCH (p:Person)
WITH p
WHERE p.age > 25
MATCH (p)-[:WORKS_FOR]->(c:Company)
RETURN p.name, c.name

// パラメータ使用
MATCH (p:Person {name: $personName})
RETURN p

Pythonでの使用例

from neo4j import GraphDatabase

# データベース接続
driver = GraphDatabase.driver(
    "bolt://localhost:7687",
    auth=("neo4j", "password")
)

def create_person(tx, name, age):
    result = tx.run(
        "CREATE (p:Person {name: $name, age: $age}) RETURN p",
        name=name, age=age
    )
    return result.single()[0]

def find_person(tx, name):
    result = tx.run(
        "MATCH (p:Person {name: $name}) RETURN p",
        name=name
    )
    return [record["p"] for record in result]

# トランザクション実行
with driver.session() as session:
    # 書き込みトランザクション
    person = session.execute_write(
        create_person, "田中太郎", 30
    )
    
    # 読み取りトランザクション
    persons = session.execute_read(
        find_person, "田中太郎"
    )
    
    for person in persons:
        print(f"Name: {person['name']}, Age: {person['age']}")

# 接続を閉じる
driver.close()

設定とチューニング

# neo4j.conf の主要設定
dbms.memory.heap.initial_size=1G
dbms.memory.heap.max_size=2G
dbms.memory.pagecache.size=1G

# セキュリティ設定
dbms.security.auth_enabled=true
dbms.ssl.policy.bolt.enabled=true

# ネットワーク設定
dbms.default_listen_address=0.0.0.0
dbms.connector.bolt.listen_address=:7687
dbms.connector.http.listen_address=:7474