CG制作データはグラフです。映画は、シーケンス、ショット、シーン、そしてアセットがすべて、依存関係によって結びついた形に分解されます。小道具(プロップ)のメッシュを変更したら、下流で何を正確に作り直す必要があるかを把握しなければなりません。つまり、リグ、アニメーションキー、そして最終的な画像シーケンスです。この影響の連鎖 は グラフそのものです。
それでも、多くのスタジオでは依然としてリレーショナルデータベースをデフォルトで使っています。
この記事では、CGパイプラインにおけるグラフデータベースの採用を後押しし、動作するPythonコード付きで、最も現実的なオープンソースの選択肢3つを順に紹介します。そして、まずどれを使い始めるべきかを判断するのに役立てます。
なぜグラフデータベースはあなたのパイプラインに必要なのか
従来のリレーショナルデータベースでは、グラフ状のデータをテーブルに平坦化することを強いられます。依存関係を辿るには、再帰的な結合(recursive joins)が必要になり、書くのが遅く、実行もさらに遅くなりがちです。
グラフデータベースは、データをノードとエッジとして保存します。これにより、パイプラインTDにとって具体的に3つの強みが得られます:
- 即時の影響分析。 監督が「このアセットを変えたら何が壊れる?」と聞いてきたとき、新しいSQLクエリを書かずに、ミリ秒単位で答えられます。
- 依存関係を考慮したビルド順序。 有向グラフがあると、トポロジカルソートが自動で利用できます。つまり、任意の要素を作り直すための正しい操作の順序は、構造に暗黙に含まれます。
- より速い反復。 変更が制作全体に波及した場合、すべてを作り直すのではなく、影響を受けた作業だけに応答して再キューできます。
Kitsuのヒント: Kitsu は、制作を通してタスクグラフをすでに追跡しています。アセット、ショット、シーケンス、そしてそれらのタスクステータスです。より深い依存関係分析のために、その上にカスタムのグラフデータベースを重ねたい場合でも、KitsuのオープンなREST APIがあれば、制作エンティティを希望するグラフストアへ同期するのは簡単です。
テスト環境
データベースを実際に比較するために、CGパイプラインを1つのプロップに適用したときの依存グラフをモデル化します(概念 → テクスチャ/メッシュ → モデル/リグ → アニメーションキー → 最終画像シーケンス)。そして代表的なクエリを10,000回実行します:
"Props 1 のメッシュが変わったら、どの要素が影響を受ける?"
すべてのベンチマークは、i7-6700 CPU @ 3.40GHz で実行し、さらにPythonクライアントのオーバーヘッドも含めます。これは、制作環境で実際にあなたが動かすのがそれだからです。
選択肢1:Neo4j
おすすめ: 頑健性とクエリのパワーが最も重要になる、制作に直結したパイプライン。
Neo4jは、利用可能なグラフデータベースの中で最も成熟しています。商用のエンタープライズ版(監視、バックアップ、HAクラスタリング)に加え、多くのスタジオのユースケースで無料で使える、堅実なコミュニティ版があります。
はじめに
Dockerでコミュニティ版を起動します:
docker run \
--publish=7474:7474 --publish=7687:7687 \
--volume=$HOME/neo4j/data:/data \
neo4j
Pythonドライバをインストールします:
pip install neo4j-driver
グラフの投入
Neo4jはCypherという、ほぼ英語を読むように扱えるよう設計されたグラフクエリ言語を使います。MERGEコマンドは「存在しなければ作成」を意味し、セットアップスクリプトを冪等(何度実行しても結果が同じ)に保ちます:
from neo4j.v1 import GraphDatabase, basic_authdriver = GraphDatabase.driver( "bolt://localhost:7687", auth=basic_auth("neo4j", "tests") ) session = driver.session()
def create_asset(name): session.run("MERGE (a:Asset { name: $name })", name=name)
def create_shot(name): session.run("MERGE (a:Shot { name: $name })", name=name)
def create_relation(asset1, asset2): session.run( "MATCH (a:Asset { name: $asset1 }), (b:Asset { name: $asset2 })" "MERGE (a)-r:ELEMENT_OF->(b)", asset1=asset1, asset2=asset2 )
def create_casting(asset, shot): session.run( "MATCH (a:Asset { name: $asset }), (b:Shot { name: $shot })" "MERGE (a)-r:CASTED_IN->(b)", asset=asset, shot=shot )
Nodes
create_asset("Props 1 concept") create_asset("Props 1 mesh") create_asset("Props 1 texture") create_asset("Props 1 rig") create_asset("Props 1 model") create_asset("Props 1 keys") create_shot("Shot 1")
Edges
create_relation("Props 1 concept", "Props 1 texture") create_relation("Props 1 concept", "Props 1 mesh") create_relation("Props 1 mesh", "Props 1 model") create_relation("Props 1 texture", "Props 1 model") create_relation("Props 1 mesh", "Props 1 rig") create_relation("Props 1 mesh", "Props 1 keys") create_relation("Props 1 rig", "Props 1 keys") create_casting("Props 1 model", "Shot 1") create_casting("Props 1 keys", "Shot 1")
影響の問い合わせ
* ワイルドカードは、1回のショット内で全ホップを辿ります。手動で再帰を管理する必要はありません:
result = session.run( "MATCH ({ name: 'Props 1 mesh' })-*->(out)" "RETURN out.name as name" ) for record in result: print(record"name")
session.close()
性能
10,000クエリ:3.5秒(セッションを開いたままにすること。毎回開き直すと約17秒かかります)。
結論
このテストにおいてNeo4jは最速で、最も表現力の高いクエリ言語を備えています。SLAsがある厳しいデッドラインの制作では、エンタープライズ版の監視・バックアップ機能の価値は十分にあります。ほとんどのスタジオではコミュニティ版で十分です。さらに、Python連携をより扱いやすくするcommunity ORMクライアントもあります。
選択肢2:ArangoDB
おすすめ: すぐに試したいスタジオ、かつグラフデータと並んでドキュメント保存も必要になるかもしれない場合。
ArangoDBはマルチモデルのデータベースで、同一エンジンでドキュメント、キー・バリュー、グラフ保存を扱います。この柔軟性により、リッチなアセットのメタデータをJSONドキュメントとして保存しつつ、同時にそれらの関係をグラフとしてモデリングできます。つまり、2つの別々のデータベースを動かす必要がありません。
はじめに
docker run -p 8529:8529 -e ARANGO_ROOT_PASSWORD=openSesame arangodb/arangodb:3.2.1
pip install python-arango
グラフスキーマの設定
ArangoDBでは、頂点コレクションとエッジ定義を明示的に定義する必要があります。エッジは常に有向です:
from arango.client import ArangoClientclient = ArangoClient(username='root', password='openSesame') db = client.create_database('cgproduction')
dependencies = db.create_graph('dependencies') shots = dependencies.create_vertex_collection('shots') assets = dependencies.create_vertex_collection('assets')
casting = dependencies.create_edge_definition( name='casting', from_collections='assets', to_collections='shots' ) elements = dependencies.create_edge_definition( name='element', from_collections='assets', to_collections='assets' )
注: ArangoDBは、すでに存在するものを作ろうとすると例外を投げます。冪等なセットアップスクリプトにするには、作成呼び出しを自前の「get or create」ヘルパーで包む必要があります。
データの投入
# Vertices assets.insert({'_key': 'props1-concept', 'name': 'Props 1 Concept'}) assets.insert({'_key': 'props1-texture', 'name': 'Props 1 Texture'}) assets.insert({'_key': 'props1-mesh', 'name': 'Props 1 Mesh'}) assets.insert({'_key': 'props1-rig', 'name': 'Props 1 Rig'}) assets.insert({'_key': 'props1-model', 'name': 'Props 1 Model'}) assets.insert({'_key': 'props1-keys', 'name': 'Props 1 Keys'}) shots.insert({'_key': 'shot1', 'name': 'Shot 1 Image Sequence'})Edges
elements.insert({'_from': 'assets/props1-concept', '_to': 'assets/props1-texture'}) elements.insert({'_from': 'assets/props1-concept', '_to': 'assets/props1-mesh'}) elements.insert({'_from': 'assets/props1-texture', '_to': 'assets/props1-model'}) elements.insert({'_from': 'assets/props1-mesh', '_to': 'assets/props1-rig'}) elements.insert({'_from': 'assets/props1-mesh', '_to': 'assets/props1-model'}) elements.insert({'_from': 'assets/props1-mesh', '_to': 'assets/props1-keys'}) elements.insert({'_from': 'assets/props1-rig', '_to': 'assets/props1-keys'}) casting.insert({'_from': 'assets/props1-model', '_to': 'shots/shot1'}) casting.insert({'_from': 'assets/props1-keys', '_to': 'shots/shot1'})
影響の問い合わせ
traversal_results = dependencies.traverse( start_vertex='assets/props1-mesh', direction='outbound' )
for result in traversal_results"vertices": print(result"name")
トラバーサルAPIは、深さ優先と幅優先のオプション、最短経路探索、パス長の取得なども公開しています。より高度なパイプライン分析に役立ちます。
性能
10,000クエリ:26秒。Neo4jより遅いものの、1セッションあたり何万ものクエリを実行しない多くのパイプラインツール用途では、十分に許容範囲です。
結論
ArangoDBは、3つの中で最も開発者にやさしい部類です。ドキュメントがよく整備されており、Pythonクライアントもきれいで、Web UIによりグラフを可視化しながら構築・デバッグしやすくなっています。ドキュメントの保存モデルが、パイプラインTDがアセットデータについて考える方法に自然に対応しています。
ArangoDBは頂点をJSONドキュメントとして保存するため、Kitsuのアセット/ショットのエンティティ(Kitsu API経由、またはgazu、Pythonクライアント)を、最小限の変換でそのままArangoDBへ直接ミラーできます。これにより、Kitsu管理の制作に依存関係の追跡を追加したい場合、ArangoDBは自然な相棒になります。
選択肢3:Cayley
おすすめ: 実験的なツールに抵抗がなく、既存のリレーショナルデータベースを軽量なグラフ探索で補完したい場合。
CayleyはGoで書かれたGoogleのグラフデータベースです。特徴は、他のストレージバックエンド(Bolt、PostgreSQLなど)の上に重ねる「レイヤー」だという点にあります。つまり、既存のデータベースを置き換えずに、グラフ機能を追加できる可能性があります。
事前に知っておくべき制約
- ドキュメントは薄い。
- Pythonクライアント(
pyley)は不完全。quadの作成は生のHTTPリクエストで行う必要があります。 - 可視化UIにバグがある。
- 再帰的トラバーサルは、まだPythonクライアントで利用できない。
はじめに
Cayleyバイナリをダウンロードし、データベースを初期化して、HTTPサーバを起動します:
./cayley init -db bolt -dbpath /tmp/testdb
./cayley http --dbpath=/tmp/testdb --host 0.0.0.0 --port 64210
pip install pyley requests
quadの投入
Cayleyはすべてをquadsとしてモデル化します:主語 → 動詞(述語) → 目的語(+ オプションのラベル)。Pythonクライアントがquad作成をサポートしていないため、REST APIを直接使います:
import requestsdef create_quad(quad): return requests.post( "http://localhost:64210/api/v1/write", json=quad )
quads = {"subject": "props1-concept", "predicate": "dependencyof", "object": "props1-texture"}, {"subject": "props1-concept", "predicate": "dependencyof", "object": "props1-mesh"}, {"subject": "props1-texture", "predicate": "dependencyof", "object": "props1-model"}, {"subject": "props1-mesh", "predicate": "dependencyof", "object": "props1-model"}, {"subject": "props1-mesh", "predicate": "dependencyof", "object": "props1-rig"}, {"subject": "props1-mesh", "predicate": "dependencyof", "object": "props1-keys"}, {"subject": "props1-rig", "predicate": "dependencyof", "object": "props1-keys"}, {"subject": "props1-model", "predicate": "dependencyof", "object": "shot1-image-sequence"}, {"subject": "props1-keys", "predicate": "dependencyof", "object": "shot1-image-sequence"},
for quad in quads: create_quad(quad)
同一のquadを再投入しても、何も起きません(ノーオペレーション)。
影響の問い合わせ
from pyley import CayleyClient, GraphObjectclient = CayleyClient("http://localhost:64210", "v1") graph = GraphObject()
query = graph.V("props1-mesh").Out().All()
性能
10,000クエリ:50秒。グループ内で最も遅い。
結論
Cayleyは、既存バックエンド上に「グラフレイヤー」をかぶせるという発想があり、設計自体もかなりエレガントです。ただし、現時点の多くのスタジオにとっては制作投入に十分ではありません。ドキュメントが乏しく、Pythonクライアントも不完全で、性能も見劣りします。このプロジェクトは見守る価値がありますが、まだ出荷(本番投入)しないでください。
クイック比較
| Neo4j | ArangoDB | Cayley | |
|---|---|---|---|
| Performance (10k queries) | 3.5s | 26s | 50s |
| Query language | Cypher (expressive) | AQL + traversal API | Gremlin / MQL |
| Python client quality | Good (+ ORM option) | Good | Incomplete |
| Documentation | Excellent | Good | Poor |
| Multi-model | No | Yes (doc + graph) | No |
| Web UI | Yes | Yes | Broken |
| Production-ready | ✅ | ✅ | ⚠️ |
| Best for | Speed & robustness | Flexibility & dev experience | Experimenting |
知っておく価値のある代替案
専用のグラフデータベースをまだ採用する準備ができていない場合、すでに使っている可能性があるツールと組み合わせる2つのアプローチがうまく機能します:
PostgreSQLの再帰的結合は、新しいデータベースを追加せずに、素直な依存関係の辿りを扱えます。クエリの複雑さはすぐに増えていきますが、初手としては有効です。
Elasticsearchは頂点とエッジをJSONドキュメントとして保存でき、グラフのようなクエリもサポートします。さらに、アセットメタデータ全体に対する全文検索やあいまい検索ができるという利点があります。検索と トラバーサルを同じ仕組みで行いたい場合に役立ちます。
グラフを可視化する
データがグラフデータベースに入ったら、最終的には自分たちのツールでそれを表示したくなるはずです。プラットフォーム別の良い選択肢:
Qt(Python/C++):
- Nodz - Python、統合が簡単
- ZodiacGraph - C++、高性能
Web/Electron:
- Cytoscape.js - 柔軟で本番レベル
- SigmaJS - 高速で、よくドキュメントされている
- D3.js - 最大限の柔軟性、学習曲線は急
KitsuのWebインターフェースでは、制作構造をすでに視覚的に分解して表示できます。エピソード、シーケンス、ショット、アセットです。カスタムツールなしで、すぐに使える制作グラフの見え方が欲しいチームにとって、Kitsuは最初からそれを提供してくれます。カスタムのグラフ可視化が最も意味を持つのは、深い技術的依存関係分析(例:アセットのバージョンが変わったときに無効化すべきレンダーファームのジョブはどれか)を行う場合です。
推奨アクションプラン
- まずはArangoDB:グラフデータベースを初めて試す場合、または素早くプロトタイプしたい場合。ドキュメントモデルと、きれいなPythonクライアントが、参入障壁として最も低いです。
- 性能が制約になったらNeo4jへ:パフォーマンスがボトルネックになるとき、またはエンタープライズ級の信頼性が必要なときです。Cypherクエリ言語は学ぶ価値があり、複雑なトラバーサルで素早く効果が出ます。
- Kitsu + gazuを使う:制作エンティティをグラフデータベースに投入します。Kitsuはアセット、ショット、タスクステータスの「唯一の正」情報源です。グラフデータベースは、その上に依存関係とビルド順序のレイヤーを追加します。
- 当面はCayleyをスキップ:12〜18か月後に見直してください。中核となる設計はしっかりしていますが、より多くのドキュメントと、より完成度の高いPythonクライアントが必要です。
- まずPostgresを検討:依存関係の要件が単純で、スタックに新しい技術を追加したくない場合。
グラフデータベースは制作管理トラッカーを置き換えるものではありませんが、変更時に何を作り直す必要があるかという点で、パイプラインをより賢くしてくれます。CG制作を Kitsuで管理しているなら、すでにアセットとショットのグラフがあります。専用のグラフデータベースを使うことで、それを完全な依存関係の追跡とビルドのオーケストレーションへ拡張できます。



