Lors de la conception d’un projet se pose toujours la question des technos à utiliser. Le but étant d’anticiper au mieux les différents besoin du projet afin d’utiliser le langage le plus pertinent pour répondre à ces besoins. Malgré la réflexion de début de projet, on tombe bien souvent sur des problématiques imprévues et très difficiles à résoudre avec le langage utilisé. Quand ces problématiques touchent aux bases de données le soucis vient souvent de la récupération de données à partir d’autres données, donc de la relation entre ces données. Pour contrer certaines de ces problématiques, on a créé les Graph Databases ! Laissez moi vous présenter aujourd’hui Neo4J et Cypher !
Cet article s’adresse à des développeurs ayant déjà manipulé des bases de données, je ne vulgariserais pas cette partie là mais uniquement la syntaxe utilisée.
Qu’est-ce qu’une Graph Database ?
Dans presque tous les projets web nous sommes amenés à créer et stocker des données de tous types. Et dans une majorité de ces projets, ces données ont besoin d’être connectées entre elles. Il existe diverses façons de connecter les données entre elles: en SQL par exemple on crée généralement une table supplémentaire qui va contenir des paires d’IDs. Ce qui implique non seulement de modifier le schéma de données à chaque fois que l’on veut ajouter une relation mais aussi d’utiliser des JOIN dans ses requêtes ce qui avec un schéma de données un peu complexe peut vite compliquer la tâche des développeurs et rallonger le temps de développement.
Les Graph Databases permettent de répondre à ces problématiques. Non seulement leur schéma n’est pas pré-défini, donc très flexible, mais elles permettent surtout de modéliser directement les relations. Elles permettent donc par exemple de faire des requêtes directement sur les relations entre les données !
Ici les différents modèles sont appelés noeuds. Chaque noeud peut avoir un ou plusieurs label (par exemple medru aura comme label User et Steloria aura comme label Company) et peut avoir des propriétés (par exemple un nom, un mail, un password, …). Les relations, elles ont un noeud de départ, un noeud d’arrivée et un type (ici la relation est de type « WORKS_AT »). Elles peuvent également avoir une ou plusieurs propriétés (par exemple la date d’embauche, la position hiérarchique, …).
Neo4J et Cypher
Développé par Neo Technology depuis l’an 2000 et sorti officiellement depuis 2010, Neo4J est une Graph Databases Open Source et écrite en Java. Elle possède son propre langage assez intuitif et efficace nommé Cypher.
(vous)-[:WORKS_AT]->(company)
Voici par exemple comment Cypher représente les relations de Neo4J ! Les noeuds sont représentés par des parenthèses () et les relations sont symbolisées par des crochets []. Autour de la relation, une flèche indique le sens de la relation (c’est vous qui travaillez dans la société, pas l’inverse). Il est aussi possible de ne pas prendre en compte le sens d’une relation, par exemple dans le cas d’une amitié comme sur Facebook la relation va dans les deux sens. Mais voyons déjà les choses dans l’ordre:
Créer une base de donnée.
Commencez par installer Neo4J sur votre machine.
Une fois l’interface ouverte, cliquez sur « Add Graph » -> « Create a Local Graph » donnez-lui un nom et un mot de passe puis cliquez sur « Create ». Une fois le Graph créé cliquez sur « Start ». Quand le Graph est lancé, cliquez sur « Neo4J Browser » en haut de l’écran. Vous pouvez maintenant commencer à écrire du code.
Créer un utilisateur:
Pour créer un noeud dans votre Graph, il vous suffit d’écrire:
//Create User
CREATE (me:User {name: "medru"})
RETURN me
Décortiquons cette ligne:
- CREATE est simplement le mot clé permettant de préciser que l’ont veut créer une nouvelle entrée dans la base. Ce mot clé fonctionne pour les noeuds comme pour les relations.
- (me) les parenthèses permettent de dire qu’il s’agit d’un noeud. « me » permet simplement de donner un nom temporaire à notre noeud, voyez ça comme une variable qui permettra de re-faire appel à notre noeud par la suite.
- :User permet de donner un label à notre noeud, ici je précise que le noeud que je crée sera un utilisateur
- {name: « medru »} entre accolades vous pouvez préciser autant de propriétés que vous voulez
- RETURN vous permet d’afficher sur l’interface les noeuds et relations que vous venez de créer ou de sélectionner, d’où l’intérêt du nom du noeud.
Récupérer un utilisateur
Maintenant que nous savons créer des entrées dans notre Graph, nous voulons les récupérer. Pour cela il faut passer par la syntaxe suivante:
//Get User
MATCH (u:User {name:"medru"})
RETURN u
Notez que les syntaxes se ressemblent beaucoup, ici nous utilisons juste la syntaxe « MATCH » pour préciser que l’on veut récupérer des données. Nous précisons ensuite que le noeud doit avoir un label User et que nous voulons un noeud dont la propriété « name » a comme valeur « medru ».
Ajouter une relation
Comme précisé auparavant, la force des Graph Databases vient de leur gestion des relations. Nous allons donc en modéliser une. Nous allons donc préciser que Medru travaille pour Wizards Technologies:
//Add company
MATCH (me:User {name:"medru"})
CREATE (me)-[w:WORKS_AT]->(wt:Company {name: "Wizards Technologies"})
RETURN me, w, wt
Ce qui nous retourne le graphe suivant:
Une fois n’est pas coutume, nous allons décomposer cette commande:
- MATCH (me:User {name: »medru »}) va tout d’abord récupérer le noeud dont le name est « medru » et le stocker dans la variable « me ».
- (me) ici nous devons obligatoirement mettre les parenthèses pour préciser qu’il s’agit d’un noeud, les variables pouvant contenir aussi bien des noeuds que des relations
- -[w:WORKS_AT]-> ici nous modélisons donc une relations de type « WORKS_AT » que nous stockons dans la variable w pour la réutiliser dans le RETURN
- (st:Company {name: « Steloria »}) ici nous créons simplement un noeud avec un nouveau label
Notez qu’avec la syntaxe CREATE vous pouvez créer plusieurs noeuds et relations directement tout en utilisant des noeuds pré-existants !
Relations multi-directionnelle
Comme précisé plus haut, on peut avoir besoin d’avoir des relations sans direction, pour cela il vous suffit de créer une relation normale et de rechercher des relations sans préciser la direction. Pour tester ça commençons déjà par créer plusieurs utilisateurs d’un coup (pas la peine d’analyser ce code, il s’agit juste de créations de plusieurs noeuds Users. J’ai simplement ajouté le fait que Patrick travaille à Google pour la suite):
//Create many users
MATCH (wt:Company {name:"Wizards Technologies"})
CREATE (n:User {name:"Quidam"})-[:WORKS_AT]->(wt), (p:User {name:"Patrick"})-[:WORKS_AT]->(g:Company {name:"Google"}), (j:User {name:"Josiane"}), (f:User {name:"Mephiboshet"}), (c:User {name:"Caroline"})
Nous allons déjà créer une première relation:
//Create friend relation
MATCH (medru:User {name:"medru"}), (quidam:User {name: "Quidam"})
CREATE (medru)-[:FRIEND_WITH]->(quidam)
Ici, comme vous pouvez le voir la relation a malgré tout une direction, cependant vous pouvez faire une recherche sans relation comme ceci:
MATCH (noeud:User {name: "Quidam"})-[f:FRIEND_WITH]-(amis:User)
RETURN noeud, f, amis
Contrairement au CREATE, le MATCH accepte les relations non directionnelles ! Ce qui nous permet de récupérer ici tous les amis de Nymeria malgré le fait que selon Neo4J c’est medru qui est ami avec Nymeria et pas l’inverse. La direction est donc obligatoire mais n’influera pas obligatoirement vos résultats de recherche.
Des recherches plus poussées
Créons tout d’abord plusieurs relations dans tous les sens:
//Create many relations
MATCH (j:User {name: "Josiane"}), (m:User {name: "medru"}), (n:User {name: "Quidam"}), (p:User {name: "Patrick"}), (c:User {name: "Caroline"}), (f:User {name: "Mephiboshet"})
CREATE (n)-[:FRIEND_WITH]->(f), (m)-[:FRIEND_WITH]->(j), (m)-[:FRIEND_WITH]->(c), (c)-
[:FRIEND_WITH]->(p)
Ce qui nous donne le graphe suivant:
Comme dernière commande de ce tutoriel, nous allons essayer de trouver le chemin le plus court entre moi et quelqu’un qui travaille chez Google. Essayez d’abord de visualiser la requête SQL que ça demanderait. Non ? Très bien, voici donc la requête en Cypher:
//Who works at google ?
MATCH (m:User {name: "medru"}), (g:User)-[:WORKS_AT]->(google:Company {name: "Google"}),
path = (m)-[:FRIEND_WITH*]-(g)
RETURN path, google
Ici je commence tout d’abord par me récupérer (medru, c’est moi) et par récupérer les utilisateurs travaillant chez Google. Ensuite via l’étoile je récupère via autant de relation [:FRIENDS_WITH] que je veux les différents chemins qui mène aux employés de chez Google et je stock ces chemins dans la variable Path avant de le retourner. Je retourne également google histoire que la société soit également visible, sinon seuls les utilisateurs le seront et savoir qui travaille chez Google ne sera pas forcément visible. Voici donc le résultat de la requête:
Voilà, si je veux un entretien chez Google il me suffit de demander à mon amie Caroline de me mettre en contact avec Patrick !
C’est tout pour aujourd’hui ! Nous n’avons vu aujourd’hui que la création et la récupération afin de vous montrer le potentiel des Graph Databases (et de Neo4J plus précisément). La prochaine fois nous verrons la modification, la suppression ainsi que quelques fonctions intéressantes de Neo4J pour aller plus loin !