Como ler esta página. Dez etapas, uma consulta por vez. Para cada uma: a consulta a digitar, o que o Neo4j Browser exibe, e o que é preciso olhar. Digite você mesmo cada consulta. Os blocos "Para entender bem" são opcionais. Se o laboratório não estiver iniciado ou se
etatnão dissernœuds : 872, volte à prática guiada, seção Em resumo (kit:https://github.com/hrhouma2/aiopsatlas-recherche-graphes-labo-fr).
Na prática guiada, você contou 872 nós e 3.712 relações carregados por um script, e olhou bolhas e setas que outra pessoa havia desenhado. Aqui, você desenha as suas. Um nó vazio primeiro, para ver que um nó é só isso; depois você lhe dá um nome, uma idade, uma cidade; depois um segundo nó, e uma seta entre os dois; depois uma seta para um curso do conjunto de dados real. No fim, o contador de etat sobe para 874, e você o traz de volta a 872 excluindo o que criou, com o erro que é preciso ter visto uma vez para entender o que é uma relação.
Um nó é uma bolha. Um rótulo diz de que tipo de bolha se trata (Personne, Cours, Ville): é o equivalente do nome da tabela. Uma propriedade é um valor escrito na bolha (nom: "Alice", age: 30): uma coluna. Uma relação é uma seta entre duas bolhas; ela tem um tipo em maiúsculas (CONNAIT, AIME), um sentido, e pode carregar suas próprias propriedades. Em SQL, a seta seria uma chave estrangeira ou uma tabela de junção; aqui é um objeto por inteiro, que você cria, lê e exclui como um nó.
| Neo4j | Banco SQL clássico | Nesta prática |
|---|---|---|
| nó | linha | Alice, Bob |
| rótulo | tabela | Personne |
| propriedade | coluna | nom, age, ville |
| relação | chave estrangeira / tabela de junção | CONNAIT, AIME |
| tipo de relação | nome da tabela de junção | CONNAIT |
| propriedade de relação | coluna da tabela de junção | depuis: 2024 |
Duas regras de escrita Cypher para memorizar: uma bolha se escreve entre parênteses (p:Personne), uma seta entre colchetes com um traço e uma ponta -[:CONNAIT]->. A letra antes dos dois-pontos (p, a, r) é um nome temporário que você escolhe para reutilizar o objeto na mesma consulta.
Abra http://localhost:7474, conecte-se (neo4j / aiopsatlas2026, URL localhost:7687). O editor é a linha neo4j$ no alto. Você envia com Ctrl + Enter (Cmd + Enter no macOS) ou o botão ▶ à direita. Cada consulta abre um quadro sob o editor, o mais recente em cima.
Um quadro exibe duas coisas. No alto, uma linha verde de balanço: Created 1 node, added 1 label, Set 1 property, Deleted 2 nodes… É ela que lhe diz o que a consulta mudou. Embaixo, se a consulta tem um RETURN, o resultado em três visões: Graph (as bolhas e as setas), Table (os valores em colunas), Raw (o JSON bruto). Uma consulta sem RETURN exibe só o balanço e Completed after … ms.
CREATE (p:Personne) RETURN pO que a consulta pede: crie uma bolha com o rótulo Personne, chame-a p durante a consulta, e mostre-a para mim.
Balanço: Created 1 node, added 1 label, depois Started streaming 1 record after 1 ms and completed after 2 ms.
O que olhar: visão Graph, uma única bolha, e à direita em Results overview: Nodes (1) com a pastilha Personne (1). Clique na bolha: o painel não lista nenhuma propriedade. É um nó mínimo: um rótulo, nada mais. Equivalente SQL: INSERT INTO personne DEFAULT VALUES, em uma tabela que não teria nenhuma coluna.
CREATE (n) sem rótulo, também funciona. O Neo4j o aceita, mas você só poderia reencontrá-la passando por todas as bolhas do grafo: uma linha sem tabela. Não faça isso; um rótulo, sempre.Personne é um rótulo novo neste grafo, que só conhecia cinco (Competence, Cours, Etudiant, Professeur, Ville). Você não precisou declarar nada: a primeira bolha que carrega um rótulo o cria. Como o mapping do Elasticsearch, na oficina fundamental 1.RETURN p não é obrigatório. Sem ele, a consulta cria a bolha e exibe só o balanço. Ele está lá para que você a veja.MATCH (p:Personne) SET p.nom = 'Alice' RETURN pO que a consulta pede: encontre as bolhas Personne (só há uma), escreva dentro nom = 'Alice', mostre-a.
Balanço: Set 1 property.
O que olhar: a bolha agora tem Alice como legenda (o Browser exibe por padrão a primeira propriedade de texto). Clique nela: nom: Alice. Equivalente SQL: UPDATE personne SET nom = 'Alice'.
MATCH antes de SET. Para modificar uma bolha, é preciso primeiro encontrá-la. MATCH (p:Personne) é o SELECT … FROM personne; SET é o UPDATE. O Cypher encadeia os dois na mesma frase.'Alice' (as aspas duplas também funcionam). Os números se escrevem sem nada: 30, não '30'.nom vazia em Bob daqui a pouco: ele simplesmente não terá a propriedade ville.MATCH (p:Personne {nom: 'Alice'}) SET p.age = 30, p.ville = 'Montréal' RETURN pO que a consulta pede: encontre a bolha Personne cujo nom é Alice, escreva dois valores a mais.
Balanço: Set 2 properties.
O que olhar: a bolha, ao clicar, mostra nom: Alice, age: 30, ville: Montréal. O {nom: 'Alice'} entre chaves é um filtro: "aquela cujo nom vale Alice", o WHERE nom = 'Alice' do SQL. Ele não muda nada aqui (uma única Personne), mas a partir da próxima etapa se torna indispensável.
CREATE (b:Personne {nom: 'Bob', age: 25}) RETURN bO que a consulta pede: crie uma bolha Personne que já tenha nom e age.
Balanço: Created 1 node, set 2 properties, added 1 label.
Olhe agora suas duas bolhas em tabela:
MATCH (p:Personne) RETURN p.nom, p.age, p.villeVisão Table:
p.nom p.age p.ville
"Alice" 30 "Montréal"
"Bob" 25 nullO que olhar: null para a cidade de Bob: ele não tem essa propriedade. Em SQL seria uma célula vazia da coluna ville; aqui, o quadro não existe de forma alguma. Equivalente SQL: SELECT nom, age, ville FROM personne.
MATCH (a:Personne {nom: 'Alice'}), (b:Personne {nom: 'Bob'})
CREATE (a)-[:CONNAIT]->(b)O que a consulta pede: encontre Alice, chame-a a; encontre Bob, chame-o b; crie uma seta do tipo CONNAIT que parte de a e chega em b.
Balanço: Created 1 relationship, depois Completed after … ms. Sem resultado: não há RETURN.
Olhe a seta:
MATCH (a:Personne)-[r]->(b:Personne) RETURN a, r, bO que a consulta pede: todas as setas, qualquer que seja seu tipo ([r] sem :TIPO), que vão de uma Personne a uma Personne.
O que olhar: visão Graph, duas bolhas e uma seta com CONNAIT escrito em cima, no sentido Alice → Bob. À direita, Results overview: Nodes (2), Relationships (1) com a pastilha CONNAIT (1). Equivalente SQL: INSERT INTO connait (personne_source, personne_cible) VALUES (id_alice, id_bob), em uma tabela de junção que você deveria ter criado antes.
MATCH na primeira linha: sem ele, CREATE (a)-[:CONNAIT]->(b) criaria duas bolhas novas e vazias e uma seta entre elas. Erro clássico._: CONNAIT, A_SUIVI, ENSEIGNE. Não é obrigatório, é o que todo mundo faz.(a)-[:CONNAIT]->(b): a ponta está em b. Não existe relação sem direção no Neo4j; na leitura, por outro lado, pode-se ignorar o sentido (etapa 7).MATCH (:Personne {nom: 'Alice'})-[r:CONNAIT]->(:Personne {nom: 'Bob'})
SET r.depuis = 2024
RETURN rO que a consulta pede: encontre a seta CONNAIT que vai de Alice a Bob, chame-a r, escreva nela depuis = 2024.
Balanço: Set 1 property.
Leia tudo em tabela:
MATCH (a:Personne)-[r:CONNAIT]->(b:Personne)
RETURN a.nom, type(r), r.depuis, b.noma.nom type(r) r.depuis b.nom
"Alice" "CONNAIT" 2024 "Bob"O que olhar: type(r) dá o tipo da seta; r.depuis sua propriedade, exatamente como p.nom para uma bolha. Uma relação se trata como um nó: encontramos, nomeamos, lemos e escrevemos dentro. As bolhas sem nome temporário, (:Personne {…}), são aquelas de que você não precisa depois: você pode omitir a letra.
MATCH (:Personne {nom: 'Bob'})-[:CONNAIT]->(x) RETURN x.nomO que a consulta pede: quem Bob conhece? As bolhas x na ponta de uma seta CONNAIT que parte de Bob.
(no changes, no records)O que olhar: zero linha. A única seta vai de Alice para Bob; nenhuma parte de Bob. Agora sem a ponta:
MATCH (:Personne {nom: 'Bob'})-[:CONNAIT]-(x) RETURN x.nomx.nom
"Alice"-[:CONNAIT]- sem >: "uma seta CONNAIT entre Bob e x, em um sentido ou no outro". Alice aparece. Memorize: na escrita, o sentido é obrigatório; na leitura, você escolhe levá-lo em conta ou não. É o que torna "quem está ligado a quem" tão curto de escrever em Cypher.
MATCH (a:Personne {nom: 'Alice'}), (c:Cours {id: 'C0001'})
CREATE (a)-[:AIME]->(c)O que a consulta pede: encontre Alice, encontre o curso C0001 do conjunto de dados, crie uma seta AIME de uma para o outro.
Balanço: Created 1 relationship.
Alice agora está ligada a uma bolha que existia antes de você. Olhe tudo o que parte dela:
MATCH (a:Personne {nom: 'Alice'})-[r]->(x)
RETURN a.nom, type(r), labels(x)[0] AS type_cible, coalesce(x.nom, x.titre) AS ciblea.nom type(r) type_cible cible
"Alice" "AIME" "Cours" "Docker expliqué simplement"
"Alice" "CONNAIT" "Personne" "Bob"O que olhar: duas linhas, dois tipos de seta, dois tipos de alvo. labels(x)[0] dá o rótulo da bolha na ponta; coalesce(x.nom, x.titre) pega nom se existir, senão titre (Bob tem um nom, o curso tem um titre). Esta é a sua tabela-entregável: guarde-a. Em SQL, seria preciso uma junção por tabela de alvo e um UNION; aqui, uma única linha de MATCH.
Passe para a visão Graph em MATCH (a:Personne {nom: 'Alice'})-[r]->(x) RETURN a, r, x: Alice no centro, Bob de um lado, o curso do outro, em duas cores (uma por rótulo).
MATCH (n) RETURN count(n) AS noeudsnoeuds
874872 + suas 2 bolhas. Por rótulo:
MATCH (n) RETURN labels(n)[0] AS label, count(*) ORDER BY labellabel count(*)
"Competence" 22
"Cours" 504
"Etudiant" 300
"Personne" 2
"Professeur" 30
"Ville" 16O que olhar: uma sexta linha, Personne 2. Em um terminal, .\labo.ps1 etat ou ./labo.sh etat exibe agora ✔ Neo4j répond — nœuds : 874. O script e o Neo4j Browser contam a mesma coisa por dois caminhos: é a verificação cruzada da prática guiada, aplicada ao que você mesmo criou.
Experimente primeiro a exclusão ingênua:
MATCH (p:Personne) DELETE pNeo.ClientError.Schema.ConstraintValidationFailed
Cannot delete node<874>, because it still has relationships. To delete this node, you must first delete its relationships.O que olhar: um quadro vermelho, nada excluído. O Neo4j se recusa a apagar uma bolha que ainda tem setas: uma seta sem bolha na ponta não teria sentido. O número node<874> é o identificador interno da primeira bolha que ele tentou excluir; na sua máquina ele difere. É o equivalente de uma violação de chave estrangeira em SQL, com uma diferença: aqui, o remédio cabe em uma palavra.
MATCH (p:Personne) DETACH DELETE pO que a consulta pede: encontre as bolhas Personne, desprenda-as (exclua suas setas), depois exclua-as.
Balanço: Deleted 2 nodes, deleted 2 relationships.
Dois nós, duas relações: CONNAIT e AIME. O curso C0001 não foi tocado, só a seta que levava a ele desapareceu. Verifique:
MATCH (n) RETURN count(n) AS noeudsnoeuds
872MATCH (n) RETURN labels(n)[0] AS label, count(*) ORDER BY label devolve novamente cinco linhas, sem Personne, e etat diz nœuds : 872.
DETACH DELETE é o gesto normal para excluir um nó no Neo4j; DELETE sozinho só serve para um nó que você sabe estar isolado, ou para excluir uma relação: MATCH ()-[r:AIME]->() DELETE r apaga a seta e guarda as bolhas.MERGE no lugar de CREATE: "crie se não existir, senão reutilize". É o que permite ao script charger-graphe ser reexecutado sem duplicata. Com CREATE, reexecutar a etapa 4 teria fabricado um segundo Bob.DELETE pratique-mini na oficina fundamental 1. Adquira o hábito: o que você cria para aprender, você exclui no fim.nom, depois age e ville, com SET, encontrando-o primeiro com MATCH.null onde uma propriedade falta.CONNAIT entre duas bolhas existentes, colocou depuis nela, e leu type(r) e r.depuis em tabela.(Bob)-[:CONNAIT]->(x) não devolve nada e (Bob)-[:CONNAIT]-(x) devolve Alice.C0001 e guardou a tabela de duas linhas da etapa 8 como entregável.Cannot delete node … because it still has relationships, depois DETACH DELETE devolveu Deleted 2 nodes, deleted 2 relationships.MATCH (n) RETURN count(n) e etat dizem 872.Invalid input, expected … em vermelho → erro de sintaxe. As três causas em sala: um parêntese ( fechado por um colchete ], uma ponta de seta esquecida na escrita (-[:CONNAIT]- em um CREATE é recusado: Only directed relationships are supported in CREATE), um apóstrofo faltando em volta de um texto.Variable `r` not defined (ou a, p…) → você usa uma letra em SET ou RETURN que nenhum MATCH nem CREATE da mesma consulta introduziu. Exemplo: SET r.depuis = 2024 quando a seta foi escrita -[:CONNAIT]-> sem o r. Acrescente a letra onde o objeto aparece: -[r:CONNAIT]->.Created 2 nodes, created 1 relationship, e Results overview mostra * (4)) → você digitou CREATE (a)-[:CONNAIT]->(b) sem o MATCH logo antes, ou com um ponto e vírgula entre as duas linhas. a e b eram desconhecidos: o Neo4j não protesta, ele os inventa, sem rótulo nem propriedade. Limpe: MATCH (n) WHERE size(labels(n)) = 0 DETACH DELETE n, depois retome a etapa 5 em uma única consulta.MATCH (p:Personne) RETURN count(p) devolve mais de 2) → você reexecutou um CREATE. MATCH (p:Personne) DETACH DELETE p e retome na etapa 1; o módulo 6 ensinará MERGE, que evita isso.Cannot delete node … because it still has relationships → é a etapa 10, intencional. DETACH DELETE.<id> em vez de Alice → o Browser ainda não sabe qual propriedade mostrar. Clique na pastilha Personne sob o grafo e escolha nom como legenda; ou leia na visão Table.etat continua dizendo nœuds : 872 após suas criações → você está conectado a outro banco ou outro Neo4j (um neo4j instalado localmente na porta 7687?). No Browser, a faixa do alto deve dizer neo4j://localhost:7687 e Database: neo4j; senão, feche o outro Neo4j ou mude sua porta.Neo.ClientError.Security.Unauthorized → senha recusada. É aiopsatlas2026; se você a mudou e perdeu, ./labo.sh reinitialiser depois demarrer e charger-graphe (os dados do kit são recarregados de forma idêntica).