Neo4J – Introduction à Cypher

Suite à mon article introduisant les concepts des bases NoSQL se trouvant ici, je vous propose une introduction à Neo4J ( http://www.neo4j.org/) qui présente les opérations de base sur Neo4J par l'exemple, via son langage de requête Cypher.

Graphe

Un graphe est composé :
  • de noeuds,
  • de relations entre ces noeuds.
Un noeud possède :
  • un nom,
  • N propriétés.
Chaque propriété est composée :
  • d'une clé : String
  • d'une valeur : Type primitif, JSON, ou tableau de ceux-ci.

Une relation est toujours unidirectionnelle.

Une relation porte une sémantique, un sens :
Pierre est ami de Paul
Paul est collègue de Martin

On est forcé de donner un nom à une relation => Obligation de la qualifier.

Une relation possède également un nom et des propriétés.
La relation est aussi importante que le noeud !

 

Why not SQL ?

Dans un système relationnel classique, la relation est portée par une clé étrangère.

Mais la clé ne qualifie pas le type de la relation.

On pourrait par exemple avoir la table Personne, avec les colonnes identifiant, identifiant père et identifiant mère. C'est la colonne qui porte l'information "père", "mère".

Pourquoi pas. Comment procède-t-on pour ajouter 1, 2, 3 frères ? Une table intermédiaire pour les relations N*N ! Qui est également nommée table de jointure.

Et si nous voulions représenter un graphe d'ami, en mémorisant l'information "ami depuis" ? Cela consisterait à ajouter une colonne dans la table de jointure. Enfin, comment connaître le nombre de bonds minimum entre deux personnes (amis en commun, plus court chemin) ?

Les bases relationnelles ne sont ni adaptées, ni destinées à ce type d'utilisation des données.

 

Because NoSQL !

Or, la base de données graphe se rapprochera mieux de la réalité :
Marie       --Mère de-->  Paul  <---Père de--Pierre
Jacques   ----frère de----^
Martin     -----frère de----|
Neo4J est une base de données de type graphe.

Elle est destinée à répondre à ce type de problématique.

Neo4J vient avec son langage de requête : Cypher. Il peut lui même être utilisé via :

  • une console d'administration Web,
  • le shell Neo4J,
  • son API REST.

Mise en bouche

Option 1 : Se rendre sur http://www.neo4j.org/learn/try
Option 2 : Installer et lancer le serveur Neo4J en local, puis accéder à la console d'administration à l'adresse par défaut : http://localhost:7474/browser/

Insertion

Ajouter un acteur (Type du noeud : Actor) nommé Tom Hanks (attribut name) :
CREATE (n:Actor { name:'Tom Hanks' });

Retrouver les acteurs nommés Tom Hanks :

MATCH (actor:Actor)
WHERE actor.name = 'Tom Hanks'
RETURN actor;
Ici, actor est un alias, valide le temps de la transaction en cours.

 

Insertion à la volée

Pour tous les acteurs dont le nom est Tom Hanks : ajouter un film (Movie) nommée Sleepless In Seattle, et ajouter une relation entre l'acteur et le film de type "A joué dans" (Acted_in), de l'acteur vers le film :

MATCH (actor:Actor)
WHERE actor.name = 'Tom Hanks'
CREATE (movie:Movie { title:'Sleepless IN Seattle' })
CREATE (actor)-[:ACTED_IN]->(movie);

Ici, la relation est représentée via : -[:ACTED_IN]->
Que l'on décrit avec -->, à l'intérieur duquel on décrit [alias:TYPE].
Avec un type ACTED_IN, et sans alias, on obtient bien une relation entre les deux noeuds désignés par leur alias.

Et si on veut être sûr de ne pas ajouter de doublon sur la relation ?
MATCH (actor:Actor)

WHERE actor.name = 'Tom Hanks'
CREATE UNIQUE (actor)-[r:ACTED_IN]->(movie:Movie { title:'Forrest Gump' })
RETURN r;

Récupérer les titres de films, avec une limite de 25 lignes retournées :
MATCH (a:Movie) WHERE has(a.title) RETURN a.title LIMIT 25

 

Update

Retrouver les acteurs nommés Tom Hanks, et ajouter l'année de naissance "1944" (DoB) :
MATCH (actor:Actor)
WHERE actor.name = 'Tom Hanks'
SET actor.DoB = 1944
RETURN actor.name, actor.DoB;
L'opération d'insertion OU mise à jour si existant est possible avec la fonctionnalité MERGE.

 

Read

Créons l'utilisateur (User), avec le champ nom (name) 'Me'.
Nous retrouvons l'utilisateur via :
MATCH (me:User)
WHERE me.name = 'Me'
RETURN me.name;
J'aimerais noter le film Matrix. Cela se passe comme suit :
MATCH (me:User),(movie:Movie)
WHERE me.name = 'Me' AND movie.title = 'The Matrix'
CREATE (me)-[:RATED { stars : 5, comment : 'I love that movie!' }]->(movie);

 

La relation contient les attributs : nombre d'étoiles, et un commentaire.
Le nombre de ces attributs peut être variable d'une relation à l'autre (même si le type de la source et de la destination sont constants).

 

Je récupère l'ensemble des titres de films que j'ai noté, avec le nombre d'étoiles et le commentaire associé :
MATCH (me:User),(me)-[rating:RATED]->(movie)
WHERE me.name = 'Me'
RETURN movie.title, rating.stars, rating.comment;
Et si on souhaitait simplement récupérer l'ensemble des relations (nom, source et destination) ?
START n=node(*)
MATCH (n)-[r]->(m)
RETURN n AS FROM , type(r) AS `->`, m AS to

L'équivalent du 'LIKE '%%' en SQL se transpose comme suit dans une requête Cypher via une expression régulière : WHERE me.name =~ '*Me*'.

Jeu de données

Lancer ces requêtes d'insertion de noeuds et de relations représentant des acteurs et des films.
CREATE (matrix1:Movie { title : 'The Matrix', year : '1999-03-31' })
CREATE (matrix2:Movie { title : 'The Matrix Reloaded', year : '2003-05-07' })
CREATE (matrix3:Movie { title : 'The Matrix Revolutions', year : '2003-10-27' })
CREATE (keanu:Actor { name:'Keanu Reeves' })
CREATE (laurence:Actor { name:'Laurence Fishburne' })
CREATE (carrieanne:Actor { name:'Carrie-Anne Moss' })
CREATE (keanu)-[:ACTS_IN { role : 'Neo' }]->(matrix1)
CREATE (keanu)-[:ACTS_IN { role : 'Neo' }]->(matrix2)
CREATE (keanu)-[:ACTS_IN { role : 'Neo' }]->(matrix3)
CREATE (laurence)-[:ACTS_IN { role : 'Morpheus' }]->(matrix1)
CREATE (laurence)-[:ACTS_IN { role : 'Morpheus' }]->(matrix2)
CREATE (laurence)-[:ACTS_IN { role : 'Morpheus' }]->(matrix3)
CREATE (carrieanne)-[:ACTS_IN { role : 'Trinity' }]->(matrix1)
CREATE (carrieanne)-[:ACTS_IN { role : 'Trinity' }]->(matrix2)
CREATE (carrieanne)-[:ACTS_IN { role : 'Trinity' }]->(matrix3)

 

Pour finir

Pour aller plus loin sur les possibilités de Cypher, se référer à : http://docs.neo4j.org/chunked/stable/cypher-query-lang.html
A l'intérieur d'un programme, on peut également effectuer un parcours sur les noeuds selon leur type, le type des relations, avec une limite de profondeur, etc. Cela s'appelle le Traversier : http://docs.neo4j.org/chunked/stable/tutorial-traversal-concepts.html
Pour de plus amples informations, je vous renvoie vers la très bonne présentation de Sylvain Roussy : http://fr.slideshare.net/lyonjug/201301-focus-neo4j

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Captcha *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.