Atelier fondamental 2 — Neo4j : un nœud, des propriétés, une relation

Pratique guidée15 min
Durée
25 min
Module
1/7
Prérequis
le labo tourne (etat affiche nœuds : 872), Neo4j Browser ouvert et connecté
Tu vas construire
deux nœuds à toi, Alice et Bob, reliés par une flèche, puis reliés au vrai graphe du cours, avant de tout effacer proprement
Livrable
la table de l'étape 8 (Alice, ses deux relations, leurs cibles)

Comment lire cette page. Dix étapes, une requête à la fois. Pour chacune : la requête à taper, ce que Neo4j Browser affiche, et ce qu'il faut regarder. Tape toi-même chaque requête. Les blocs « Pour bien comprendre » sont facultatifs. Si le labo n'est pas démarré ou si etat ne dit pas nœuds : 872, retourne à la pratique guidée, section En bref (kit : https://github.com/hrhouma2/aiopsatlas-recherche-graphes-labo-fr).

Objectif

Dans la pratique guidée, tu as compté 872 nœuds et 3 712 relations chargés par un script, et tu as regardé des bulles et des flèches que quelqu'un d'autre avait dessinées. Ici, tu dessines les tiennes. Un nœud vide d'abord, pour voir qu'un nœud c'est juste ça ; puis tu lui donnes un nom, un âge, une ville ; puis un deuxième nœud, et une flèche entre les deux ; puis une flèche vers un cours du vrai jeu de données. À la fin, le compteur de etat monte à 874, et tu le ramènes à 872 en supprimant ce que tu as créé, avec l'erreur qu'il faut avoir vue une fois pour comprendre ce qu'est une relation.

Le vocabulaire en une image

Un nœud est une bulle. Une étiquette dit de quel genre de bulle il s'agit (Personne, Cours, Ville) : c'est l'équivalent du nom de la table. Une propriété est une valeur écrite dans la bulle (nom: "Alice", age: 30) : une colonne. Une relation est une flèche entre deux bulles ; elle a un type en majuscules (CONNAIT, AIME), un sens, et peut porter ses propres propriétés. En SQL, la flèche serait une clé étrangère ou une table de jointure ; ici c'est un objet à part entière, que tu crées, lis et supprimes comme un nœud.

Neo4jBase SQL classiqueDans cette pratique
nœudligneAlice, Bob
étiquettetablePersonne
propriétécolonnenom, age, ville
relationclé étrangère / table de jointureCONNAIT, AIME
type de relationnom de la table de jointureCONNAIT
propriété de relationcolonne de la table de jointuredepuis: 2024

Deux règles d'écriture Cypher à retenir : une bulle s'écrit entre parenthèses (p:Personne), une flèche entre crochets avec un tiret et une pointe -[:CONNAIT]->. La lettre devant les deux-points (p, a, r) est un nom temporaire que tu choisis pour réutiliser l'objet dans la même requête.

Où taper, et comment lire la réponse

Ouvre http://localhost:7474, connecte-toi (neo4j / aiopsatlas2026, URL localhost:7687). L'éditeur est la ligne neo4j$ en haut. Tu envoies avec Ctrl + Entrée (Cmd + Entrée sur macOS) ou le bouton ▶ à droite. Chaque requête ouvre un cadre sous l'éditeur, le plus récent en haut.

Un cadre affiche deux choses. En haut, une ligne verte de bilan : Created 1 node, added 1 label, Set 1 property, Deleted 2 nodes… C'est elle qui te dit ce que la requête a changé. En dessous, si la requête a un RETURN, le résultat en trois vues : Graph (les bulles et les flèches), Table (les valeurs en colonnes), Raw (le JSON brut). Une requête sans RETURN n'affiche que le bilan et Completed after … ms.

Étape 1 — Créer un nœud, juste une étiquette

cypher
CREATE (p:Personne) RETURN p

Ce que la requête demande : crée une bulle portant l'étiquette Personne, appelle-la p le temps de la requête, et montre-la-moi.

Bilan : Created 1 node, added 1 label, puis Started streaming 1 record after 1 ms and completed after 2 ms.

À regarder : vue Graph, une seule bulle, et à droite dans Results overview : Nodes (1) avec la pastille Personne (1). Clique sur la bulle : le panneau ne liste aucune propriété. C'est un nœud minimal : une étiquette, rien d'autre. Équivalent SQL : INSERT INTO personne DEFAULT VALUES, dans une table qui n'aurait aucune colonne.

Pour bien comprendre
  • Encore plus minimal, CREATE (n) sans étiquette, marche aussi. Neo4j l'accepte, mais tu ne pourrais plus la retrouver qu'en passant par toutes les bulles du graphe : une ligne sans table. Ne le fais pas ; une étiquette, toujours.
  • Personne est une étiquette nouvelle dans ce graphe, qui n'en connaissait que cinq (Competence, Cours, Etudiant, Professeur, Ville). Tu n'as rien eu à déclarer : la première bulle qui porte une étiquette la crée. Comme le mapping d'Elasticsearch, à l'atelier fondamental 1.
  • RETURN p n'est pas obligatoire. Sans lui, la requête crée la bulle et n'affiche que le bilan. Il est là pour que tu la voies.

Étape 2 — Lui donner une valeur

cypher
MATCH (p:Personne) SET p.nom = 'Alice' RETURN p

Ce que la requête demande : trouve les bulles Personne (il n'y en a qu'une), écris dedans nom = 'Alice', montre-la.

Bilan : Set 1 property.

À regarder : la bulle porte maintenant Alice en légende (Browser affiche par défaut la première propriété texte). Clique dessus : nom: Alice. Équivalent SQL : UPDATE personne SET nom = 'Alice'.

Pour bien comprendre
  • MATCH avant SET. Pour modifier une bulle, il faut d'abord la trouver. MATCH (p:Personne) est le SELECT … FROM personne ; SET est le UPDATE. Cypher enchaîne les deux dans la même phrase.
  • Les textes sont entre apostrophes simples 'Alice' (les guillemets doubles marchent aussi). Les nombres s'écrivent nus : 30, pas '30'.
  • Une propriété n'existe que si elle a une valeur. Il n'y a pas de colonne nom vide chez Bob tout à l'heure : il n'aura simplement pas de propriété ville.

Étape 3 — Plusieurs valeurs d'un coup

cypher
MATCH (p:Personne {nom: 'Alice'}) SET p.age = 30, p.ville = 'Montréal' RETURN p

Ce que la requête demande : trouve la bulle Personne dont le nom est Alice, écris deux valeurs de plus.

Bilan : Set 2 properties.

À regarder : la bulle, en cliquant, montre nom: Alice, age: 30, ville: Montréal. Le {nom: 'Alice'} entre accolades est un filtre : « celle dont nom vaut Alice », le WHERE nom = 'Alice' de SQL. Il ne change rien ici (une seule Personne), mais dès l'étape suivante il devient indispensable.

Étape 4 — Créer un nœud avec ses valeurs en une fois

cypher
CREATE (b:Personne {nom: 'Bob', age: 25}) RETURN b

Ce que la requête demande : crée une bulle Personne qui a tout de suite nom et age.

Bilan : Created 1 node, set 2 properties, added 1 label.

Regarde maintenant tes deux bulles en tableau :

cypher
MATCH (p:Personne) RETURN p.nom, p.age, p.ville

Vue Table :

text
p.nom    p.age  p.ville
"Alice"  30     "Montréal"
"Bob"    25     null

À regarder : null pour la ville de Bob : il n'a pas cette propriété. En SQL ce serait une cellule vide de la colonne ville ; ici, la case n'existe pas du tout. Équivalent SQL : SELECT nom, age, ville FROM personne.

Étape 5 — Relier Alice à Bob

cypher
MATCH (a:Personne {nom: 'Alice'}), (b:Personne {nom: 'Bob'})
CREATE (a)-[:CONNAIT]->(b)

Ce que la requête demande : trouve Alice, appelle-la a ; trouve Bob, appelle-le b ; crée une flèche de type CONNAIT qui part de a et arrive sur b.

Bilan : Created 1 relationship, puis Completed after … ms. Pas de résultat : il n'y a pas de RETURN.

Regarde la flèche :

cypher
MATCH (a:Personne)-[r]->(b:Personne) RETURN a, r, b

Ce que la requête demande : toutes les flèches, quel que soit leur type ([r] sans :TYPE), qui vont d'une Personne à une Personne.

À regarder : vue Graph, deux bulles et une flèche avec CONNAIT écrit dessus, dans le sens Alice → Bob. À droite, Results overview : Nodes (2), Relationships (1) avec la pastille CONNAIT (1). Équivalent SQL : INSERT INTO connait (personne_source, personne_cible) VALUES (id_alice, id_bob), dans une table de jointure que tu aurais dû créer avant.

Pour bien comprendre
  • Une relation relie deux bulles qui existent déjà. D'où le MATCH en première ligne : sans lui, CREATE (a)-[:CONNAIT]->(b) créerait deux bulles neuves et vides et une flèche entre elles. Erreur classique.
  • Le type est en MAJUSCULES par convention, avec des _ : CONNAIT, A_SUIVI, ENSEIGNE. Ce n'est pas obligatoire, c'est ce que tout le monde fait.
  • Une relation a toujours un sens. (a)-[:CONNAIT]->(b) : la pointe est sur b. Il n'existe pas de relation sans direction dans Neo4j ; à la lecture, en revanche, on peut ignorer le sens (étape 7).

Étape 6 — Une valeur sur la flèche

cypher
MATCH (:Personne {nom: 'Alice'})-[r:CONNAIT]->(:Personne {nom: 'Bob'})
SET r.depuis = 2024
RETURN r

Ce que la requête demande : trouve la flèche CONNAIT qui va d'Alice à Bob, appelle-la r, écris dessus depuis = 2024.

Bilan : Set 1 property.

Lis tout en tableau :

cypher
MATCH (a:Personne)-[r:CONNAIT]->(b:Personne)
RETURN a.nom, type(r), r.depuis, b.nom
text
a.nom    type(r)    r.depuis  b.nom
"Alice"  "CONNAIT"  2024      "Bob"

À regarder : type(r) donne le type de la flèche ; r.depuis sa propriété, exactement comme p.nom pour une bulle. Une relation se traite comme un nœud : on la trouve, on la nomme, on lit et on écrit dedans. Les bulles sans nom temporaire, (:Personne {…}), sont celles dont tu n'as pas besoin après : tu peux omettre la lettre.

Étape 7 — La flèche a un sens

cypher
MATCH (:Personne {nom: 'Bob'})-[:CONNAIT]->(x) RETURN x.nom

Ce que la requête demande : qui Bob connaît-il ? Les bulles x au bout d'une flèche CONNAIT qui part de Bob.

text
(no changes, no records)

À regarder : zéro ligne. La seule flèche va d'Alice vers Bob ; aucune ne part de Bob. Maintenant sans la pointe :

cypher
MATCH (:Personne {nom: 'Bob'})-[:CONNAIT]-(x) RETURN x.nom
text
x.nom
"Alice"

-[:CONNAIT]- sans > : « une flèche CONNAIT entre Bob et x, dans un sens ou dans l'autre ». Alice apparaît. Retiens : à l'écriture, le sens est obligatoire ; à la lecture, tu choisis d'en tenir compte ou pas. C'est ce qui rend « qui est lié à qui » si court à écrire en Cypher.

Étape 8 — Relier ta bulle au vrai graphe

cypher
MATCH (a:Personne {nom: 'Alice'}), (c:Cours {id: 'C0001'})
CREATE (a)-[:AIME]->(c)

Ce que la requête demande : trouve Alice, trouve le cours C0001 du jeu de données, crée une flèche AIME de l'une vers l'autre.

Bilan : Created 1 relationship.

Alice est maintenant reliée à une bulle qui existait avant toi. Regarde tout ce qui part d'elle :

cypher
MATCH (a:Personne {nom: 'Alice'})-[r]->(x)
RETURN a.nom, type(r), labels(x)[0] AS type_cible, coalesce(x.nom, x.titre) AS cible
text
a.nom    type(r)    type_cible  cible
"Alice"  "AIME"     "Cours"     "Docker expliqué simplement"
"Alice"  "CONNAIT"  "Personne"  "Bob"

À regarder : deux lignes, deux types de flèche, deux types de cible. labels(x)[0] donne l'étiquette de la bulle au bout ; coalesce(x.nom, x.titre) prend nom s'il existe, sinon titre (Bob a un nom, le cours a un titre). C'est ta table-livrable : garde-la. En SQL, il aurait fallu une jointure par table de cible et un UNION ; ici, une seule ligne de MATCH.

Passe en vue Graph sur MATCH (a:Personne {nom: 'Alice'})-[r]->(x) RETURN a, r, x : Alice au centre, Bob d'un côté, le cours de l'autre, en deux couleurs (une par étiquette).

Étape 9 — Compter

cypher
MATCH (n) RETURN count(n) AS noeuds
text
noeuds
874

872 + tes 2 bulles. Par étiquette :

cypher
MATCH (n) RETURN labels(n)[0] AS label, count(*) ORDER BY label
text
label         count(*)
"Competence"  22
"Cours"       504
"Etudiant"    300
"Personne"    2
"Professeur"  30
"Ville"       16

À regarder : une sixième ligne, Personne 2. Dans un terminal, .\labo.ps1 etat ou ./labo.sh etat affiche maintenant ✔ Neo4j répond — nœuds : 874. Le script et Neo4j Browser comptent la même chose par deux chemins : c'est la vérification croisée de la pratique guidée, appliquée à ce que tu as créé toi-même.

Étape 10 — Nettoyer, et rencontrer l'erreur qui explique tout

Essaie d'abord la suppression naïve :

cypher
MATCH (p:Personne) DELETE p
text
Neo.ClientError.Schema.ConstraintValidationFailed
Cannot delete node<874>, because it still has relationships. To delete this node, you must first delete its relationships.

À regarder : un cadre rouge, rien supprimé. Neo4j refuse d'effacer une bulle qui a encore des flèches : une flèche sans bulle au bout n'aurait pas de sens. Le numéro node<874> est l'identifiant interne de la première bulle qu'il a tenté de supprimer ; chez toi il diffère. C'est l'équivalent d'une violation de clé étrangère en SQL, avec une différence : ici, le remède tient en un mot.

cypher
MATCH (p:Personne) DETACH DELETE p

Ce que la requête demande : trouve les bulles Personne, détache-les (supprime leurs flèches), puis supprime-les.

Bilan : Deleted 2 nodes, deleted 2 relationships.

Deux nœuds, deux relations : CONNAIT et AIME. Le cours C0001 n'a pas été touché, seule la flèche qui y menait a disparu. Vérifie :

cypher
MATCH (n) RETURN count(n) AS noeuds
text
noeuds
872

MATCH (n) RETURN labels(n)[0] AS label, count(*) ORDER BY label rend à nouveau cinq lignes, sans Personne, et etat dit nœuds : 872.

Pour bien comprendre
  • DETACH DELETE est le geste normal pour supprimer un nœud dans Neo4j ; DELETE seul ne sert que pour un nœud dont tu sais qu'il est isolé, ou pour supprimer une relation : MATCH ()-[r:AIME]->() DELETE r efface la flèche et garde les bulles.
  • Le module 6 te fera écrire MERGE à la place de CREATE : « crée si ça n'existe pas, sinon réutilise ». C'est ce qui permet au script charger-graphe d'être rejoué sans doublon. Avec CREATE, relancer l'étape 4 aurait fabriqué un deuxième Bob.
  • Rien de ce que tu as fait n'est resté. Le graphe est revenu à son état de référence, exactement comme après DELETE pratique-mini à l'atelier fondamental 1. Prends l'habitude : ce que tu crées pour apprendre, tu le supprimes à la fin.

Vérification finale

  • Tu as créé un nœud avec une seule étiquette et vu qu'il n'a aucune propriété.
  • Tu lui as donné nom, puis age et ville, avec SET, en le retrouvant d'abord avec MATCH.
  • Tu as créé Bob avec ses valeurs en une requête, et vu null là où une propriété manque.
  • Tu as créé CONNAIT entre deux bulles existantes, mis depuis dessus, et lu type(r) et r.depuis en table.
  • Tu sais dire pourquoi (Bob)-[:CONNAIT]->(x) ne rend rien et (Bob)-[:CONNAIT]-(x) rend Alice.
  • Tu as relié Alice au cours C0001 et gardé la table à deux lignes de l'étape 8 comme livrable.
  • Tu as vu l'erreur Cannot delete node … because it still has relationships, puis DETACH DELETE a rendu Deleted 2 nodes, deleted 2 relationships.
  • MATCH (n) RETURN count(n) et etat disent 872.

Si ça coince

Afficher les cas fréquents
  • Invalid input, expected … en rouge → faute de syntaxe. Les trois causes en salle : une parenthèse ( fermée par un crochet ], une pointe de flèche oubliée à l'écriture (-[:CONNAIT]- dans un CREATE est refusé : Only directed relationships are supported in CREATE), une apostrophe manquante autour d'un texte.
  • Variable `r` not defined (ou a, p…) → tu utilises une lettre dans SET ou RETURN qu'aucun MATCH ni CREATE de la même requête n'a introduite. Exemple : SET r.depuis = 2024 alors que la flèche a été écrite -[:CONNAIT]-> sans le r. Ajoute la lettre là où l'objet apparaît : -[r:CONNAIT]->.
  • L'étape 5 a créé des bulles vides en plus (Created 2 nodes, created 1 relationship, et Results overview montre * (4)) → tu as tapé CREATE (a)-[:CONNAIT]->(b) sans le MATCH juste avant, ou avec un point-virgule entre les deux lignes. a et b étaient inconnus : Neo4j ne proteste pas, il les invente, sans étiquette ni propriété. Nettoie : MATCH (n) WHERE size(labels(n)) = 0 DETACH DELETE n, puis reprends l'étape 5 en une seule requête.
  • Deux Alice ou deux Bob (MATCH (p:Personne) RETURN count(p) rend plus de 2) → tu as relancé un CREATE. MATCH (p:Personne) DETACH DELETE p et reprends à l'étape 1 ; le module 6 t'apprendra MERGE, qui évite ça.
  • Cannot delete node … because it still has relationships → c'est l'étape 10, voulue. DETACH DELETE.
  • La bulle affiche un numéro ou <id> au lieu de Alice → Browser ne sait pas encore quelle propriété montrer. Clique sur la pastille Personne sous le graphe et choisis nom comme légende ; ou lis en vue Table.
  • etat dit toujours nœuds : 872 après tes créations → tu es connecté à une autre base ou un autre Neo4j (un neo4j installé en local sur le port 7687 ?). Dans Browser, le bandeau du haut doit dire neo4j://localhost:7687 et Database: neo4j ; sinon, ferme l'autre Neo4j ou change son port.
  • Neo.ClientError.Security.Unauthorized → mot de passe refusé. C'est aiopsatlas2026 ; si tu l'as changé et perdu, ./labo.sh reinitialiser puis demarrer et charger-graphe (les données du kit sont rechargées à l'identique).