Backend Gremlin¶
ddigraph écrit dans toute base de données graphe qui parle Gremlin, le langage de requête d'Apache TinkerPop.
Bases de données prises en charge¶
| Base de données | Connexion | Usage |
|---|---|---|
| Apache TinkerGraph | En mémoire, via Gremlin Server | Tests locaux, développement |
| JanusGraph | WebSocket | Production, distribué |
| Amazon Neptune | WebSocket | Cloud AWS, service géré |
| Azure Cosmos DB | WebSocket | Cloud Azure, API Gremlin |
Dépendances¶
gremlinpython est un extra optionnel :
pip install "ddigraph[gremlin]"
Pour essayer en local, lancez l'image officielle du serveur :
docker run --rm -p 8182:8182 tinkerpop/gremlin-server:3.8.2
Charger du DDI¶
write_gremlin prend une source de traversée et un fichier. Elle renvoie le
nombre de sommets et d'arêtes écrits.
from gremlin_python.driver.driver_remote_connection import DriverRemoteConnection
from gremlin_python.process.anonymous_traversal import traversal
from ddigraph.backends.gremlin import write_gremlin
connection = DriverRemoteConnection("ws://localhost:8182/gremlin", "g")
g = traversal().with_(connection)
result = write_gremlin(g, "survey.xml")
print(result.nodes, "sommets,", result.relationships, "arêtes")
connection.close()
Le fichier peut être du DDI XML de n'importe quelle variante, ou un export
RDF comme survey.ttl. Vous pouvez aussi passer des blocs déjà obtenus avec
ddigraph.iter_graph().
Ce qui est écrit¶
- Chaque sommet a une
node_key. C'est l'identité du nœud, sous forme de chaîne. Trouvez un sommet par son label et sa clé :g.V().has("Variable", "node_key", "v1"). - Le chargement peut être répété. Chaque écriture est un upsert. Un sommet n'est créé que si aucun sommet n'a déjà ce label et cette clé. Une arête n'est créée que si le même type ne relie pas déjà les deux mêmes sommets. Chargez un fichier deux fois : rien ne change.
labeletidsont renommés enddi_labeletddi_id. Certaines bases traitent ces noms comme le label et l'identifiant de l'élément lui-même. Cosmos DB les refuse purement et simplement.- Les listes deviennent des chaînes, jointes avec
|. Les bases diffèrent trop dans leur façon de stocker les listes pour s'y fier.
Options¶
| Option | Défaut | Description |
|---|---|---|
batch_size |
50 |
Sommets ou arêtes par requête |
cardinality |
"single" |
Remplacer une valeur au rechargement. Passez None si votre base ne gère que les propriétés de type liste |
flavor |
détectée | Forcer une variante DDI |
dataset_id |
nom du fichier | Identifiant du jeu de données pour un Codebook |
Le résultat a aussi un compte skipped. Il compte les arêtes non écrites
faute de sommet d'extrémité. Il doit valoir zéro. Sinon, le journal indique
combien.
Vitesse¶
Chaque requête coûte un aller-retour vers le serveur : les écritures sont
donc groupées. Sur un grand graphe, indexez d'abord node_key : chaque
upsert cherche un sommet par cette propriété. La façon d'ajouter un index
dépend de la base. JanusGraph passe par son API de gestion. Neptune indexe
déjà chaque propriété.
Requêtes Gremlin¶
Traversées de base¶
// Compter les sommets par label
g.V().groupCount().by(label)
// Trouver tous les QuestionItems
g.V().hasLabel('QuestionItem').valueMap(true)
// Obtenir un nœud par sa clé
g.V().has('QuestionItem', 'node_key', 'urn:ddi:test.org:q1:1.0').valueMap(true)
Traversées de relations¶
// De l'Instrument à toutes ses constructions
g.V().hasLabel('Instrument').out('HAS_CONSTRUCT').valueMap(true)
// Questions avec leurs listes de codes
g.V().hasLabel('QuestionItem')
.as('q')
.out('USES_CODELIST')
.as('cl')
.select('q', 'cl')
.by(valueMap('ddi_label', 'question_text'))
.by(valueMap('ddi_label'))
// Catégories d'une liste de codes
g.V().has('CodeList', 'node_key', 'urn:ddi:test.org:cl1:1.0')
.out('HAS_CATEGORY')
.valueMap('category_label')
Requêtes de chemins¶
// Chemin complet de l'Instrument aux questions
g.V().hasLabel('Instrument')
.repeat(out('HAS_CONSTRUCT'))
.until(hasLabel('QuestionConstruct'))
.out('REFERENCES_QUESTION')
.path()
.by('ddi_label')
// Branches conditionnelles
g.V().hasLabel('IfThenElse')
.project('then', 'else')
.by(out('THEN').values('ddi_label').fold())
.by(out('ELSE').values('ddi_label').fold())
Configuration propre à chaque base¶
JanusGraph¶
from gremlin_python.driver.serializer import GraphSONSerializersV3d0
connection = DriverRemoteConnection(
"ws://localhost:8182/gremlin", "g", message_serializer=GraphSONSerializersV3d0()
)
Amazon Neptune¶
connection = DriverRemoteConnection(
"wss://your-cluster.region.neptune.amazonaws.com:8182/gremlin", "g"
)
Avec l'authentification IAM, signez la requête WebSocket avec SigV4. La documentation AWS explique comment.
Azure Cosmos DB¶
from gremlin_python.driver import serializer
connection = DriverRemoteConnection(
"wss://your-account.gremlin.cosmos.azure.com:443/",
"g",
username="/dbs/your-database/colls/your-graph",
password="your-primary-key",
message_serializer=serializer.GraphSONSerializersV2d0(),
)
Voir aussi¶
- NetworkX - Le même graphe, en mémoire
- pandas - Le même graphe, en deux tableaux
- Modèle de relations - Types de relations DDI
- Documentation Apache TinkerPop