Les données de production CG sont un graphe : un film se décompose en séquences, plans, scènes et assets, tous liés entre eux par des relations de dépendance. Modifiez la “maillage” d’un prop, et vous devez savoir exactement ce qui doit être reconstruit en aval : le rig, les clés d’animation, la séquence d’images finale. Cette chaîne d’impact est un graphe.
Et pourtant, la plupart des studios utilisent encore par défaut des bases de données relationnelles.
Cet article défend l’usage des bases de données orientées graphes dans les pipelines CG, passe en revue les trois options open source les plus viables avec du code Python fonctionnel, et vous aide à décider laquelle essayer en premier.
Pourquoi les bases de données orientées graphes ont leur place dans votre pipeline
Une base de données relationnelle traditionnelle vous oblige à “aplatir” des données en forme de graphe dans des tables. Le parcours des dépendances nécessite alors des jointures récursives, à la fois difficiles à écrire et plus lentes à exécuter.
Les bases de données orientées graphes stockent vos données sous forme de nœuds et d’arêtes. Elles débloquent trois gains concrets pour les TD de pipeline :
- Analyse d’impact instantanée. Quand un réalisateur demande “qu’est-ce qui casse si je change cet asset ?”, vous pouvez répondre en quelques millisecondes au lieu d’écrire une nouvelle requête SQL.
- Ordonnancement de construction sensible aux dépendances. Un graphe orienté vous donne un tri topologique “gratuitement” : la bonne séquence d’opérations pour reconstruire n’importe quel élément est implicite dans la structure.
- Itérations plus rapides. Quand un changement se propage dans votre production, vous pouvez répondre et ré-queue uniquement le travail concerné plutôt que de tout reconstruire.
Astuce Kitsu : Kitsu suit déjà le graphe de tâches à travers toute votre production : assets, plans, séquences, et leurs statuts de tâches. Si vous voulez ajouter au-dessus une base de données orientée graphes sur mesure pour une analyse des dépendances plus poussée, l’API REST open source de Kitsu rend la synchronisation des entités de production vers votre magasin de graphes de choix assez simple.
Le dispositif de test
Pour comparer les bases de données de manière concrète, on modélise le graphe de dépendance d’un seul prop passant par un pipeline CG (concept → texture/maillage → model/rig → clés d’animation → séquence d’images finale), puis on exécute une requête représentative 10 000 fois :
« Quels éléments sont impactés si le maillage de Props 1 change ? »
Tous les benchmarks tournent sur un CPU i7-6700 @ 3,40 GHz et incluent la surcharge du client Python, puisque c’est exactement ce que vous exécuterez en production.
Option 1 : Neo4j
Idéal pour : les pipelines critiques pour la production, où la robustesse et la puissance de requête comptent le plus.
Neo4j est la base de données orientée graphes la plus mature. Elle propose une offre enterprise commerciale (avec monitoring, sauvegardes et clustering HA) et une édition community solide, gratuite pour la plupart des cas d’usage en studio.
Bien démarrer
Lancez l’édition community via Docker :
docker run \
--publish=7474:7474 --publish=7687:7687 \
--volume=$HOME/neo4j/data:/data \
neo4j
Installez le driver Python :
pip install neo4j-driver
Remplir le graphe
Neo4j utilise Cypher, un langage de requêtes orienté graphes conçu pour lire presque comme de l’anglais. La commande MERGE agit comme “créer si n’existe pas”, ce qui rend vos scripts de configuration idempotents :
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")
Requêter pour l’impact
Le joker * traverse tous les “hops” en une seule fois, sans récursion à gérer manuellement :
result = session.run( "MATCH ({ name: 'Props 1 mesh' })-*->(out)" "RETURN out.name as name" ) for record in result: print(record"name")
session.close()
Performances
10 000 requêtes : 3,5 secondes (gardez la session ouverte ; la rouvrir à chaque fois coûte environ ~17 secondes).
Verdict
Neo4j est l’option la plus rapide dans ce test et offre le langage de requête le plus expressif. Si vous êtes sur une production sous contrainte de deadline avec des SLAs, les fonctions de monitoring et de sauvegarde de l’édition enterprise valent le coût. Pour la plupart des studios, l’édition community suffit largement. Il existe aussi un client ORM community qui rend l’intégration Python plus ergonomique.
Option 2 : ArangoDB
Idéal pour : les studios qui veulent expérimenter rapidement et qui pourraient aussi avoir besoin de stockage de documents en plus des données de graphes.
ArangoDB est une base de données multi-modèles qui gère documents, clé-valeur et stockage de graphes dans un seul moteur. Cette flexibilité signifie que vous pouvez stocker des métadonnées d’assets riches sous forme de documents JSON et modéliser les relations entre eux comme un graphe, sans exécuter deux bases de données séparées.
Bien démarrer
docker run -p 8529:8529 -e ARANGO_ROOT_PASSWORD=openSesame arangodb/arangodb:3.2.1
pip install python-arango
Mettre en place le schéma du graphe
ArangoDB vous oblige à définir explicitement les collections de sommets (vertices) et les définitions des arêtes (edges). Les edges sont toujours dirigées :
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' )
Note : ArangoDB lève une exception si vous essayez de créer quelque chose qui existe déjà. Vous devrez encapsuler vos appels de création dans vos propres helpers “get or create” pour des scripts de configuration idempotents.
Insérer des données
# 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'})
Requêter pour l’impact
traversal_results = dependencies.traverse( start_vertex='assets/props1-mesh', direction='outbound' )
for result in traversal_results"vertices": print(result"name")
L’API de traversal expose aussi des options “depth-first” versus “breadth-first”, la recherche de plus court chemin, et la récupération de la longueur des chemins. Pratique pour une analyse avancée du pipeline.
Performances
10 000 requêtes : 26 secondes. Plus lent que Neo4j, mais tout à fait acceptable pour la plupart des outils de pipeline qui ne lancent pas des dizaines de milliers de requêtes par session.
Verdict
ArangoDB est le plus agréable à utiliser du lot côté développeurs. La documentation est bonne, le client Python est propre, et l’interface web permet de visualiser et de déboguer facilement votre graphe pendant sa construction. Le modèle de stockage des documents correspond naturellement à la façon dont les TD de pipeline pensent déjà les données d’assets.
Parce qu’ArangoDB stocke les sommets comme des documents JSON, vous pouvez directement refléter les entités “asset” et “shot” de Kitsu (récupérées via l’API de Kitsu ou gazu, le client Python) dans ArangoDB avec une transformation minimale. Cela fait d’ArangoDB un compagnon naturel de Kitsu si vous voulez ajouter un suivi des dépendances à votre production gérée par Kitsu.
Option 3 : Cayley
Idéal pour : compléter une base de données relationnelle existante avec un parcours de graphe léger, si vous êtes à l’aise avec un outil expérimental.
Cayley est une base de données orientée graphes de Google, écrite en Go. Sa caractéristique principale est d’être une couche au-dessus d’autres moteurs de stockage (Bolt, PostgreSQL, etc.), ce qui signifie que vous pourriez ajouter des capacités de graphe sans remplacer votre base de données actuelle.
Limitations à connaître d’emblée
- La documentation est légère.
- Le client Python (
pyley) est incomplet : la création des quads doit se faire via des requêtes HTTP brutes. - L’interface de visualisation est boguée.
- Le traversal récursif n’est pas encore disponible dans le client Python.
Bien démarrer
Téléchargez le binaire de Cayley, initialisez la base de données, puis démarrez le serveur HTTP :
./cayley init -db bolt -dbpath /tmp/testdb
./cayley http --dbpath=/tmp/testdb --host 0.0.0.0 --port 64210
pip install pyley requests
Insérer des quads
Cayley modélise tout comme des quads : sujet → prédicat → objet (+ étiquette optionnelle). Comme le client Python ne supporte pas la création de quads, utilisez directement l’API REST :
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)
Réinsérer des quads identiques ne fait rien (no-op).
Requêter pour l’impact
from pyley import CayleyClient, GraphObjectclient = CayleyClient("http://localhost:64210", "v1") graph = GraphObject()
query = graph.V("props1-mesh").Out().All()
Performances
10 000 requêtes : 50 secondes. Le plus lent du groupe.
Verdict
Cayley a une conception vraiment élégante et l’idée d’une couche de graphes au-dessus d’un backend existant est convaincante. Mais elle n’est pas prête pour la production dans la plupart des studios aujourd’hui : la documentation est clairsemée, le client Python est incomplet, et les performances sont en retrait. Surveillez ce projet, mais ne le déployez pas encore.
Comparaison rapide
| Neo4j | ArangoDB | Cayley | |
|---|---|---|---|
| Performance (10k requêtes) | 3.5s | 26s | 50s |
| Langage de requêtes | Cypher (expressif) | AQL + API de traversal | Gremlin / MQL |
| Qualité du client Python | Bon (+ option ORM) | Bon | Incomplet |
| Documentation | Excellente | Bonne | Faible |
| Multi-modèle | Non | Oui (doc + graphe) | Non |
| Interface web | Oui | Oui | Brisée |
| Prêt pour la production | ✅ | ✅ | ⚠️ |
| Idéal pour | Vitesse & robustesse | Flexibilité & expérience développeur | Expérimentation |
Des alternatives à connaître
Si vous n’êtes pas prêt à adopter une base de données orientée graphes dédiée, deux approches fonctionnent bien avec des outils que vous avez peut-être déjà :
Les jointures récursives de PostgreSQL peuvent gérer un parcours de dépendances simple sans ajouter une nouvelle base de données à votre pile. La complexité des requêtes augmente rapidement, mais c’est un point de départ valable.
Elasticsearch peut stocker des sommets et des arêtes sous forme de documents JSON et supporte des requêtes de type “graphe”. Il ajoute l’avantage d’une recherche en texte intégral et approximative sur vos métadonnées d’assets. Utile si vous voulez à la fois rechercher et parcourir dans le même système.
Visualiser votre graphe
Une fois vos données dans une base de données orientée graphes, vous voudrez forcément les rendre dans vos propres outils. Bonnes options selon la plateforme :
Qt (Python/C++) :
- Nodz - Python, facile à intégrer
- ZodiacGraph - C++, haute performance
Web/Electron :
- Cytoscape.js - polyvalent et adapté à la production
- SigmaJS - rapide, bien documenté
- D3.js - flexibilité maximale, courbe d’apprentissage plus raide
L’interface web de Kitsu fournit déjà une visualisation de la structure de production : épisodes, séquences, plans et assets. Pour les équipes qui veulent une vue “graph” de production déjà prête sans outil sur mesure, Kitsu vous le donne out of the box. Les visualisations de graphes personnalisées ont le plus de sens pour une analyse technique profonde des dépendances (par exemple : quels jobs de ferme de rendu invalider quand une version d’asset change).
Plan d’action recommandé
- Commencez par ArangoDB si vous explorez les bases de données orientées graphes pour la première fois ou si vous voulez prototyper rapidement. Son modèle de documents et son client Python propre réduisent au maximum la barrière d’entrée.
- Passez à Neo4j quand les performances deviennent une contrainte ou quand vous avez besoin d’une fiabilité niveau enterprise. Le langage de requêtes Cypher vaut la peine d’être appris : le gain est rapide pour les traversals complexes.
- Utilisez Kitsu + gazu pour initialiser votre base de données orientée graphes avec les entités de production. Kitsu est votre source de vérité pour les assets, les plans et les statuts de tâches ; votre base de données orientée graphes ajoute par-dessus la couche de dépendances et d’ordonnancement.
- Pour l’instant, mettez Cayley de côté. Revenez y dans 12 à 18 mois : la conception de base est saine, mais il faut plus de documentation et un client Python plus complet.
- Envisagez d’abord Postgres si vous avez des besoins simples en dépendances et que vous voulez éviter d’ajouter une nouvelle technologie à votre pile.
Les bases de données orientées graphes ne remplaceront pas votre outil de suivi de production, mais elles rendront votre pipeline plus intelligent sur ce qu’il faut reconstruire quand quelque chose change. Si vous gérez la production CG avec Kitsu, vous avez déjà le graphe des assets et des plans ; une base de données orientée graphes dédiée vous permet de l’étendre à un suivi complet des dépendances et à l’orchestration de la construction.




