データベース
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