Πώς να διαβάσεις αυτή τη σελίδα. Κάθε ενότητα είναι διπλωμένη κάτω από τον τίτλο της: κάνε κλικ στο «Afficher …» για να την ανοίξεις, και ξανάκλεισέ την όταν τελειώσεις για να κρατήσεις τη σελίδα ευανάγνωστη. Σειρά ανάγνωσης: Objectif (Στόχος), μετά En bref (Εν συντομία, οι εντολές που θα πληκτρολογήσεις), μετά Le jeu de données (Το σύνολο δεδομένων, να διαβαστεί πριν από κάθε ερώτημα), μετά τα ερωτήματα Elasticsearch και Neo4j, ταξινομημένα από το απλούστερο (
GET _cat/indices,MATCH (n) RETURN n) στο πιο εντυπωσιακό, με μια εξήγηση μετά το καθένα. Το λεπτομερές βήμα προς βήμα, με την αναμενόμενη έξοδο κάθε εντολής και τις βλάβες προς πρόκληση, βρίσκεται σε παράρτημα: παράρτημα Α για Windows (PowerShell), παράρτημα Β για Linux, macOS, WSL 2 και Git Bash. Άνοιξε μόνο ένα παράρτημα, αυτό του συστήματός σου. Το παράρτημα Γ, κοινό, συγκεντρώνει τις περιπτώσεις που κολλάνε.
Εντάσσεσαι στην ομάδα που κατασκευάζει τη μηχανή αναζήτησης της πλατφόρμας διαδικτυακών μαθημάτων. Η προϊσταμένη σου σου δίνει το kit του εργαστηρίου: «Αύριο το πρωί, θέλω ένα εργαστήριο που να τρέχει στον σταθμό σου, τα δεδομένα φορτωμένα, και τη βεβαιότητα ότι ξέρεις να το επιδιορθώνεις χωρίς να με καλέσεις.» Θα ξεκινήσεις λοιπόν το stack, θα αποδείξεις ότι οι τρεις κινητήρες περιέχουν αυτό που πρέπει, μετά θα σταματήσεις μια υπηρεσία επίτηδες για να δεις πώς διαβάζεται η βλάβη στο etat, στο πρόγραμμα περιήγησης και στο αρχείο καταγραφής, και θα την επαναφέρεις σε λειτουργία. Το να αναγνωρίζεις «αυτή η υπηρεσία είναι σταματημένη» σε δέκα δευτερόλεπτα είναι αυτό που αποτρέπει ώρες αναζήτησης στο λάθος μέρος.
Τα εννέα βήματα αυτού του διαγράμματος περιγράφονται λεπτομερώς, με την αναμενόμενη έξοδο κάθε εντολής, στο παράρτημα Α (Windows) ή στο παράρτημα Β (Linux, macOS) στο τέλος της σελίδας.
Kit του εργαστηρίου: https://github.com/hrhouma2/aiopsatlas-recherche-graphes-labo-fr
Κλωνοποιείς το kit σε έναν φάκελο lab1, επαληθεύεις ότι το Docker είναι έτοιμο, ξεκινάς τις τρεις υπηρεσίες (Elasticsearch, Kibana, Neo4j), ανοίγεις τις τρεις ιστοσελίδες τους, μετά φορτώνεις τα δεδομένα. Εκτελείς το importer και το charger-graphe δύο φορές: η δεύτερη δεν πρέπει να αλλάξει τίποτα στους μετρητές, αυτή είναι η απόδειξη ότι η φόρτωση επαναλαμβάνεται χωρίς διπλότυπο. Στο τέλος, το etat πρέπει να εμφανίζει (healthy) παντού, acces 12000 avis 609 cours 504 και nœuds : 872. Ξεκίνα εκτελώντας αυτό το μπλοκ.
Windows (PowerShell)
git clone https://github.com/hrhouma2/aiopsatlas-recherche-graphes-labo-fr.git lab1
cd lab1
ls # explorer le contenu : docker-compose.yml, labo.ps1, labo.sh, elasticsearch/, neo4j/, outils/
.\labo.ps1 prerequis
.\labo.ps1 demarrer
.\labo.ps1 etatΕπαλήθευσε τα τρία URL στο πρόγραμμα περιήγησης:
Kibana http://localhost:5601 (Dev Tools : menu ☰ → Management → Dev Tools)
Elasticsearch http://localhost:9200
Neo4j Browser http://localhost:7474 (utilisateur neo4j · mot de passe aiopsatlas2026).\labo.ps1 importer
.\labo.ps1 charger-graphe
.\labo.ps1 importer # 2e passage : mêmes compteurs, rien ne double
.\labo.ps1 charger-graphe
.\labo.ps1 etat # attendu : acces 12000 avis 609 cours 504 nœuds : 872Αν το PowerShell αρνείται το .\labo.ps1 («η εκτέλεση σεναρίων είναι απενεργοποιημένη»): Set-ExecutionPolicy -Scope CurrentUser RemoteSigned, απάντησε O, ξανατρέξε.
Linux, macOS, WSL 2, Git Bash
git clone https://github.com/hrhouma2/aiopsatlas-recherche-graphes-labo-fr.git lab1
cd lab1
ls # explorer le contenu : docker-compose.yml, labo.sh, labo.ps1, elasticsearch/, neo4j/, outils/
./labo.sh prerequis
./labo.sh demarrer
./labo.sh etatΕπαλήθευσε τα τρία URL στο πρόγραμμα περιήγησης:
Kibana http://localhost:5601 (Dev Tools : menu ☰ → Management → Dev Tools)
Elasticsearch http://localhost:9200
Neo4j Browser http://localhost:7474 (utilisateur neo4j · mot de passe aiopsatlas2026)./labo.sh importer
./labo.sh charger-graphe
./labo.sh importer # 2e passage : mêmes compteurs, rien ne double
./labo.sh charger-graphe
./labo.sh etat # attendu : acces 12000 avis 609 cours 504 nœuds : 872Πριν πληκτρολογήσεις έστω ένα ερώτημα, κοίταξε τα δεδομένα. Όλο το εργαστήριο περιστρέφεται γύρω από μια φανταστική πλατφόρμα διαδικτυακών μαθημάτων: έναν κατάλογο μαθημάτων, τις κριτικές που αφήνουν οι μαθητές, το αρχείο καταγραφής του διακομιστή ιστού που εξυπηρετεί τις σελίδες, και τους συνδέσμους μεταξύ μαθητών, καθηγητών, μαθημάτων και δεξιοτήτων. Τα ίδια δεδομένα φορτώνονται στο Elasticsearch (για αναζήτηση) και στο Neo4j (για παρακολούθηση των συνδέσμων). Βρίσκονται στο kit, σε καθαρή μορφή, σε δύο φακέλους:
lab1/
├── elasticsearch/donnees/ ← ce qui va dans Elasticsearch (3 fichiers NDJSON)
│ ├── cours.ndjson 504 cours
│ ├── avis.ndjson 609 avis
│ └── acces.ndjson 12 000 lignes de journal web
└── neo4j/import/ ← ce qui va dans Neo4j (8 fichiers CSV)
├── cours.csv 504 cours → nœuds Cours
├── etudiants.csv 300 étudiants → nœuds Etudiant
├── professeurs.csv 30 professeurs → nœuds Professeur
├── competences.csv 22 compétences → nœuds Competence
├── villes.csv 16 villes → nœuds Ville
├── inscriptions.csv 1 654 inscriptions → relations INSCRIT_A
├── couvre.csv 994 liens → relations COUVRE
└── prerequis.csv 230 liens → relations PREREQUIS_DEΆνοιξέ τα ο ίδιος, παίρνει δέκα δευτερόλεπτα και θα ξέρεις ακριβώς τι χειρίζεσαι:
# Windows (PowerShell), depuis le dossier lab1
Get-Content elasticsearch\donnees\cours.ndjson -TotalCount 2
Get-Content elasticsearch\donnees\avis.ndjson -TotalCount 2
Get-Content elasticsearch\donnees\acces.ndjson -TotalCount 2
Get-Content neo4j\import\cours.csv -TotalCount 3
Get-Content neo4j\import\inscriptions.csv -TotalCount 3# Linux, macOS, WSL 2, Git Bash, depuis le dossier lab1
head -n 2 elasticsearch/donnees/cours.ndjson
head -n 2 elasticsearch/donnees/avis.ndjson
head -n 2 elasticsearch/donnees/acces.ndjson
head -n 3 neo4j/import/cours.csv
head -n 3 neo4j/import/inscriptions.csvΈνα αρχείο .ndjson (newline-delimited JSON) περιέχει ένα αντικείμενο JSON ανά γραμμή. Στο kit, οι γραμμές πάνε ανά δύο: μια γραμμή εντολής («τοποθέτησε το παρακάτω στο index cours με το αναγνωριστικό C0001»), μετά το ίδιο το έγγραφο. Είναι η μορφή που στέλνει η εντολή importer στο Elasticsearch μαζικά.
{"index":{"_index":"cours","_id":"C0001"}}
{"id":"C0001","titre":"Docker expliqué simplement","description":"…","categorie":"DevOps", … }cours: 504 έγγραφα, ένα ανά μάθημα του καταλόγουΤο πρώτο έγγραφο, όπως είναι στο Elasticsearch:
{
"id": "C0001",
"titre": "Docker expliqué simplement",
"description": "Dans ce cours accessible sans prérequis, vous apprenez à automatiser vos applications avec Docker. …",
"categorie": "DevOps",
"sujet": "Docker",
"niveau": "debutant",
"langue": "en",
"prix": 129,
"gratuit": false,
"duree_heures": 5,
"tags": ["docker", "linux", "helm", "devops"],
"date_publication": "2024-08-14",
"note_moyenne": 4.4,
"nb_avis": 327,
"professeur": { "id": "P001", "nom": "Karim Caron", "ville": "Gatineau" },
"competences": ["Conteneurisation", "Intégration continue"]
}| Πεδίο | Παράδειγμα | Τι είναι |
|---|---|---|
id | C0001 | Αναγνωριστικό του μαθήματος. C όπως cours (μάθημα), μετά ένας αριθμός. Είναι επίσης το _id του εγγράφου. |
titre | Docker expliqué simplement | Ο τίτλος. Ελεύθερο κείμενο, πάνω σε αυτό θα γίνονται οι αναζητήσεις με λέξεις. |
description | Dans ce cours… | Μια παράγραφος παρουσίασης. Επίσης ελεύθερο κείμενο. |
categorie | DevOps | Μία από τις έξι μεγάλες οικογένειες: Cloud, DevOps, Données (Δεδομένα), Développement web (Ανάπτυξη ιστού), IA (ΤΝ), Sécurité (Ασφάλεια). 84 μαθήματα η καθεμία. |
sujet | Docker | Πιο συγκεκριμένο από την κατηγορία: Docker, Kubernetes, Neo4j, Elasticsearch… |
niveau | debutant | debutant (αρχάριος), intermediaire ή avance. |
langue | en | fr ή en. |
prix | 129 | Σε δολάρια. 0 για δωρεάν μάθημα. |
gratuit | false | Αληθές ή ψευδές. |
duree_heures | 5 | Συνολική διάρκεια του μαθήματος. |
tags | ["docker", "linux", …] | Μια λίστα λέξεων-κλειδιών. Ένα πεδίο μπορεί να περιέχει πολλές τιμές. |
date_publication | 2024-08-14 | Μια ημερομηνία. |
note_moyenne | 4.4 | Μέσος όρος των βαθμολογιών, από το 5. |
nb_avis | 327 | Αριθμός κριτικών που ελήφθησαν. |
professeur | { "id": "P001", "nom": …, "ville": … } | Ένα αντικείμενο μέσα στο αντικείμενο: ο καθηγητής περιγράφεται απευθείας μέσα στην κάρτα του μαθήματος, με το αναγνωριστικό, το όνομα και την πόλη του. |
competences | ["Conteneurisation", …] | Λίστα των δεξιοτήτων που καλύπτει το μάθημα. |
avis: 609 έγγραφα, ένα ανά κριτική που άφησε ένας μαθητής{
"id": "A00001",
"cours_id": "C0028",
"etudiant": "Nathan",
"ville": "Sherbrooke",
"pays": "Canada",
"note": 3,
"texte": "Les vidéos sont bonnes, la partie théorique est dense.",
"date": "2024-06-21",
"utile": 33
}| Πεδίο | Παράδειγμα | Τι είναι |
|---|---|---|
id | A00001 | Αναγνωριστικό της κριτικής. A όπως avis (κριτική). |
cours_id | C0028 | Το σχετικό μάθημα. Είναι ο σύνδεσμος προς το index cours: αυτό το C0028 είναι το id ενός εγγράφου του cours. |
etudiant | Nathan | Το όνομα του συγγραφέα. |
ville, pays | Sherbrooke, Canada | Από πού γράφει. |
note | 3 | Η βαθμολογία που δόθηκε, από 1 έως 5. |
texte | Les vidéos sont bonnes… | Το σχόλιο. Ελεύθερο κείμενο. |
date | 2024-06-21 | Ημερομηνία της κριτικής. |
utile | 33 | Αριθμός ατόμων που βρήκαν αυτή την κριτική χρήσιμη. |
acces: 12.000 έγγραφα, μία γραμμή ανά αίτημα που έλαβε ο διακομιστής ιστού{
"id": "L000001",
"@timestamp": "2026-08-10T08:17:19.000Z",
"methode": "GET",
"chemin": "/robots.txt",
"cours_id": null,
"categorie": null,
"statut": 200,
"octets": 108506,
"duree_ms": 100,
"ip": "108.190.166.1",
"pays": "CA",
"appareil": "desktop",
"navigateur": "Edge",
"referent": "google"
}| Πεδίο | Παράδειγμα | Τι είναι |
|---|---|---|
id | L000001 | Αναγνωριστικό της γραμμής. L όπως ligne (γραμμή) αρχείου καταγραφής (log). |
@timestamp | 2026-08-10T08:17:19.000Z | Ακριβής ημερομηνία και ώρα του αιτήματος. Το @ είναι σύμβαση: είναι το πεδίο χρόνου που το Kibana εντοπίζει αυτόματα. |
methode | GET | GET ή POST. |
chemin | /robots.txt | Η διεύθυνση που ζητήθηκε στον ιστότοπο: /, /cours, /catalogue, /contact, /tarifs, ή η σελίδα ενός μαθήματος. |
cours_id | null ή C0042 | Το μάθημα που εμφανίστηκε, αν η σελίδα αφορά κάποιο· null (κενό) αλλιώς, όπως εδώ για το /robots.txt. Σύνδεσμος προς το index cours. |
categorie | null ή DevOps | Κατηγορία του μαθήματος που εμφανίστηκε, αντιγραμμένη για να απλοποιεί τα γραφήματα. |
statut | 200 | Ο κωδικός HTTP της απάντησης: 200 OK, 301 και 304 ανακατεύθυνση ή cache, 404 δεν βρέθηκε, 500 και 503 σφάλμα διακομιστή. |
octets | 108506 | Μέγεθος της απάντησης που στάλθηκε. |
duree_ms | 100 | Χρόνος απόκρισης σε χιλιοστά του δευτερολέπτου. |
ip | 108.190.166.1 | Διεύθυνση IP του επισκέπτη. |
pays | CA | Χώρα του επισκέπτη, κωδικός δύο γραμμάτων. |
appareil | desktop | desktop, mobile ή tablette (tablet). |
navigateur | Edge | Chrome, Firefox, Safari, Edge… |
referent | google | Από πού ήρθε ο επισκέπτης: direct, google, linkedin, youtube ή newsletter. |
Τα τρία index είναι συνδεδεμένα μέσω του cours_id: μια κριτική μιλά για ένα μάθημα, μια γραμμή αρχείου καταγραφής επισκέπτεται ένα μάθημα. Αλλά το Elasticsearch δεν κάνει join: κάθε index αναζητείται ξεχωριστά. Για να παρακολουθήσουμε τους συνδέσμους, περνάμε στο Neo4j.
Το Neo4j λαμβάνει τα ίδια μαθήματα (ίδια αναγνωριστικά C0001…, ίδιοι τίτλοι), συν ό,τι δεν υπάρχει στο Elasticsearch: τους μαθητές, τις εγγραφές τους, τους καθηγητές, τις δεξιότητες, τις πόλεις, και τα προαπαιτούμενα μεταξύ μαθημάτων. Το μοντέλο χωράει σε ένα σχέδιο:
Τα πέντε βέλη διαβάζονται σαν φράσεις: ένας μαθητής είναι εγγεγραμμένος σε ένα μάθημα· ένας καθηγητής διδάσκει ένα μάθημα· ένα μάθημα καλύπτει μια δεξιότητα· ένα μάθημα είναι προαπαιτούμενο ενός άλλου· ένας μαθητής ή καθηγητής κατοικεί σε μια πόλη. Συνολικά 872 κόμβοι και 3.712 σχέσεις.
Τα δεδομένα φτάνουν με τη μορφή κλασικών αρχείων CSV, μια γραμμή κεφαλίδας μετά μια γραμμή ανά στοιχείο:
cours.csv id,titre,categorie,sujet,niveau,prix,duree_heures,date_publication,professeur_id
C0001,Docker expliqué simplement,DevOps,Docker,debutant,129,5,2024-08-14,P001
etudiants.csv id,prenom,nom,ville,pays,inscription_le,interet
E0001,Nathan,Ben Ali,Sherbrooke,Canada,2023-05-15,DevOps
professeurs.csv id,prenom,nom,ville,pays,specialite,annees_experience
P001,Karim,Caron,Gatineau,Canada,DevOps,23
competences.csv id,nom
K01,Conteneurisation
villes.csv nom,pays,latitude,longitude
Montréal,Canada,45.5019,-73.5674
inscriptions.csv etudiant_id,cours_id,date,progression,note
E0001,C0028,2024-04-01,100,3
couvre.csv cours_id,competence_id
C0001,K01
prerequis.csv prerequis_id,cours_id
C0001,C0003Κοίτα πώς μεταφράζεται αυτό σε γράφο. Τα πέντε πρώτα αρχεία γίνονται κόμβοι· κάθε γραμμή είναι κόμβος, κάθε στήλη ιδιότητα. Τα τρία τελευταία γίνονται σχέσεις· κάθε γραμμή συνδέει δύο κόμβους μέσω των αναγνωριστικών τους. Η γραμμή E0001,C0028,2024-04-01,100,3 του inscriptions.csv γίνεται το βέλος (Etudiant E0001)-[:INSCRIT_A {date, progression, note}]->(Cours C0028): ο Nathan Ben Ali είναι εγγεγραμμένος στο μάθημα C0028, το ολοκλήρωσε στο 100%, και του έβαλε 3. Σύγκρινε με την πρώτη κριτική του index avis: είναι ο ίδιος Nathan, το ίδιο μάθημα C0028, η ίδια βαθμολογία 3.
| Κόμβος | Ιδιότητες | Παράδειγμα |
|---|---|---|
Cours | id, titre, categorie, sujet, niveau, prix, duree_heures, date_publication | C0001, «Docker expliqué simplement», 129 $ |
Etudiant | id, prenom, nom, interet, inscription_le | E0001, Nathan Ben Ali, ενδιαφέρον DevOps |
Professeur | id, prenom, nom, specialite, annees_experience | P001, Karim Caron, DevOps, 23 έτη |
Competence | id, nom | K01, Conteneurisation |
Ville | nom, pays, latitude, longitude | Montréal, Canada |
| Σχέση | Από → προς | Ιδιότητες | Σημασία |
|---|---|---|---|
INSCRIT_A | Etudiant → Cours | date, progression, note | Ο μαθητής παρακολουθεί αυτό το μάθημα |
ENSEIGNE | Professeur → Cours | — | Αυτός ο καθηγητής διδάσκει αυτό το μάθημα (ένας μόνο ανά μάθημα) |
COUVRE | Cours → Competence | — | Αυτό το μάθημα διδάσκει αυτή τη δεξιότητα |
PREREQUIS_DE | Cours → Cours | — | Πρέπει να έχεις παρακολουθήσει το πρώτο πριν το δεύτερο |
HABITE | Etudiant ή Professeur → Ville | — | Πού μένει το άτομο |
Όλα τα δεδομένα χρησιμοποιούν την ίδια σύμβαση: ένα γράμμα, μετά έναν αριθμό. Όταν βλέπεις ένα αναγνωριστικό, ξέρεις αμέσως για τι μιλάμε.
| Πρόθεμα | Πράγμα | Παράδειγμα | Πού το βρίσκεις |
|---|---|---|---|
C | Μάθημα | C0001 | Elasticsearch cours, avis.cours_id, acces.cours_id, Neo4j Cours |
A | Κριτική | A00001 | Elasticsearch avis |
L | Γραμμή αρχείου καταγραφής | L000001 | Elasticsearch acces |
E | Μαθητής | E0001 | Neo4j Etudiant |
P | Καθηγητής | P001 | cours.professeur.id στο Elasticsearch, Neo4j Professeur |
K | Δεξιότητα | K01 | Neo4j Competence |
Κράτα ένα μόνο νήμα για όλη τη συνέχεια: το μάθημα C0001, «Docker expliqué simplement», που διδάσκεται από τον P001, Karim Caron. Θα το ξαναβρείς στο Elasticsearch (ερώτημα E9), στο Neo4j (ερωτήματα N7 και N13), και θα χρησιμεύσει για να αποδείξεις ότι οι δύο κινητήρες περιέχουν όντως τα ίδια δεδομένα.
Οι μετρητές του etat αποδεικνύουν ότι τα δεδομένα είναι εκεί· τα παρακάτω ερωτήματα τα κάνουν ορατά. Είναι όλα μόνο για ανάγνωση: μπορείς να τα ξανατρέξεις όσες φορές θέλεις, τίποτα δεν θα τροποποιηθεί. Είναι ίδια σε Windows και σε Linux, όλα γίνονται στο πρόγραμμα περιήγησης.
Ο κανόνας αυτής της ενότητας: μόνο μία καινοτομία ανά ερώτημα. Ξεκινάμε με «δείξε μου τι υπάρχει», χωρίς καμία παράμετρο, και προσθέτουμε μια έννοια σε κάθε βήμα. Μετά από κάθε ερώτημα, μια σύντομη εξήγηση· όταν χρειάζεται εμβάθυνση, ένα διπλωμένο πλαίσιο «Για να καταλάβεις καλά». Επικόλλησε τα ερωτήματα ένα προς ένα, με τη σειρά, και διάβασε την απάντηση πριν προχωρήσεις στο επόμενο.
Άνοιξε http://localhost:5601, μετά μενού ☰ → Management → Dev Tools (ή «Outils de développement»). Το αριστερό πλαίσιο είναι ένας επεξεργαστής: επικόλλησε ένα ερώτημα, τοποθέτησε τον δρομέα πάνω του, μετά Ctrl+Enter ή το τρίγωνο ▶. Η απάντηση εμφανίζεται δεξιά, με τον κωδικό HTTP (200 - OK) κάτω.
Μια εικόνα να κρατήσεις στο μυαλό σου για όλη τη συνέχεια: το Elasticsearch είναι ένα μεγάλο ντουλάπι.
L'armoire = Elasticsearch (Το ντουλάπι = Elasticsearch)
Un tiroir = un index (le tiroir « cours », le tiroir « avis », le tiroir « acces »)
(Ένα συρτάρι = ένα index)
Une fiche = un document (une fiche par cours, une fiche par avis, une fiche par ligne de journal)
(Μία κάρτα = ένα έγγραφο)
_cat/indices = lire les étiquettes collées sur les tiroirs : nom, état, nombre de fiches, épaisseur
(διαβάζεις τις ετικέτες στα συρτάρια)
_search = ouvrir un tiroir et lire les fiches qui sont dedans
(ανοίγεις ένα συρτάρι και διαβάζεις τις κάρτες)Τα ερωτήματα Ε1 έως Ε4 κοιτάζουν τις ετικέτες των συρταριών. Από το Ε5 και μετά, ανοίγουμε τα συρτάρια. Μην τα μπερδεύεις ποτέ: είναι το λάθος νούμερο ένα των αρχάριων.
GET _cat/indicesΤι ζητά το ερώτημα: «Elasticsearch, δείξε μου τη λίστα όλων των συρταριών σου.» Απάντηση στο μηχάνημα του μαθήματος:
green open avis W9j_JrwJT4mdzpWcS5k7xg 1 0 609 0 74.6kb 74.6kb 74.6kb
green open .internal.alerts-security.alerts-default-000001 YJq3VGeRQKqD0fqJW6Fw3w 1 0 0 0 249b 249b 249b
green open acces aii68fsfQXKyt5wqkE1mPA 1 0 12000 0 1.5mb 1.5mb 1.5mb
green open cours pmq403ZgSZWeHJY9uNw1Qw 1 0 504 0 183.6kb 183.6kb 183.6kbΤέσσερις γραμμές = τέσσερα συρτάρια. Τρία είναι δικά σου: avis, acces, cours. Το τέταρτο, αυτό του οποίου το όνομα ξεκινά με τελεία (.internal.alerts-security…), δεν είναι δικό σου: το Kibana το δημιούργησε μόνο του για εσωτερική χρήση (διαχείριση ειδοποιήσεων ασφαλείας). Είναι άδειο (0 κάρτες), δεν ενοχλεί, μην το διαγράψεις. Στη συνέχεια, θα το παραλείπουμε από την εμφάνιση.
Αυτό που είναι ενοχλητικό εδώ: υπάρχουν αριθμοί παντού και καμία κεφαλίδα στήλης. Είναι σαν πίνακας Excel χωρίς γραμμή κεφαλίδας. Το διορθώνουμε στο Ε2.
Για να διαβάσεις το ίδιο το ερώτημα, λέξη προς λέξη:
| Κομμάτι | Τι σημαίνει |
|---|---|
GET | «Θέλω να διαβάσω κάτι.» Δεν τροποποιώ τίποτα, δεν διαγράφω τίποτα, κοιτάζω. Χωρίς κίνδυνο. |
_cat | «Απάντησέ μου σε πίνακα κειμένου», αναγνώσιμο από άνθρωπο, όχι σε JSON. Η κάτω παύλα στην αρχή δηλώνει μια εντολή του ίδιου του Elasticsearch, όχι όνομα index. |
indices | «… τον πίνακα των index.» Είναι ο αγγλικός πληθυντικός του index. |
Ένα index, είναι ένα συρτάρι: ένα μέρος όπου τακτοποιούμε κάρτες που μοιάζουν μεταξύ τους. Όλες οι κάρτες «μαθήματα» πάνε στο συρτάρι cours, όλες οι κάρτες «κριτικές» στο συρτάρι avis, και κάθε γραμμή του αρχείου καταγραφής του διακομιστή ιστού είναι μια κάρτα του συρταριού acces. Αν ξέρεις SQL, το index είναι αυτό που η SQL ονομάζει πίνακα:
| Elasticsearch | Στην εικόνα | SQL |
|---|---|---|
Index cours | Το συρτάρι «μαθήματα» | Πίνακας cours |
| Έγγραφο JSON | Μια κάρτα στο συρτάρι | Γραμμή |
| Πεδίο | Ένα κουτάκι στην κάρτα (τίτλος, τιμή…) | Στήλη |
_id του εγγράφου | Ο αριθμός γραμμένος πάνω στην κάρτα | Πρωτεύον κλειδί |
Τα τρία συρτάρια του εργαστηρίου:
Elasticsearch
├── index cours → 504 documents (un document = un cours du catalogue)
├── index avis → 609 documents (un document = un avis laissé par un étudiant)
└── index acces → 12 000 documents (un document = une ligne de journal du serveur web)Δύο παγίδες λεξιλογίου. Ο ενικός είναι ένα index, και ο πληθυντικός είναι index (πολλά) στα ελληνικά (το Elasticsearch γράφει indices, τον αγγλικό πληθυντικό). Και δεν είναι «ένδειξη» με τη μαθηματική ή αστυνομική έννοια: είναι index όπως αυτό στο τέλος ενός βιβλίου, η λίστα που λέει σε ποια σελίδα βρίσκεται κάθε λέξη. Το Elasticsearch κάνει ακριβώς αυτό, σε πολύ μεγάλη κλίμακα.
Τώρα, ας πάρουμε μία μόνο γραμμή από την απάντηση Ε1 και ας τη διαβάσουμε τιμή προς τιμή, από αριστερά προς τα δεξιά:
green open avis W9j_JrwJT4mdzpWcS5k7xg 1 0 609 0 74.6kb 74.6kb 74.6kb| Τιμή | Τι σημαίνει |
|---|---|
green | Το συρτάρι είναι σε καλή κατάσταση. Πράσινο = όλα καλά. |
open | Το συρτάρι είναι ανοιχτό: μπορείς να διαβάσεις και να γράψεις μέσα. (Το αντίθετο θα ήταν close.) |
avis | Το όνομα του συρταριού. |
W9j_JrwJT4mdzpWcS5k7xg | Ένας τεχνικός σειριακός αριθμός, που δημιουργείται από το Elasticsearch. Δεν θα τον χρησιμοποιήσεις ποτέ. |
1 | Το συρτάρι είναι σε ένα μόνο κομμάτι (ένα κύριο shard). Ένα μεγάλο index μπορεί να χωριστεί σε πολλά κομμάτια κατανεμημένα σε πολλά μηχανήματα· εδώ, όχι. |
0 | Μηδέν αντίγραφα ασφαλείας (replica). Φυσιολογικό σε ένα εργαστήριο με ένα μόνο μηχάνημα: ένα αντίγραφο δεν θα είχε πού να πάει. |
609 | Το συρτάρι περιέχει 609 κάρτες. Είναι η τιμή που κοιτάμε πρώτη. |
0 | Μηδέν κάρτες σημειωμένες «προς πέταγμα» σε αναμονή καθαρισμού. |
74.6kb | Συνολικός χώρος στον δίσκο. |
74.6kb | Χώρος του κύριου κομματιού μόνο (ίδιος: υπάρχει μόνο ένα κομμάτι και κανένα αντίγραφο). |
74.6kb | Μέγεθος των ίδιων των δεδομένων. |
Τρεις φορές 74.6kb επειδή, με ένα μόνο κομμάτι και μηδέν αντίγραφα, οι τρεις τρόποι μέτρησης δίνουν το ίδιο αποτέλεσμα. Σε ένα πραγματικό cluster με αντίγραφα, το store.size θα ήταν μεγαλύτερο από το pri.store.size.
Συνοπτικά, τα τρία σου συρτάρια:
avis → 609 fiches → 74,6 Ko
acces → 12 000 fiches → 1,5 Mo
cours → 504 fiches → 183,6 Ko?vGET _cat/indices?vhealth status index uuid pri rep docs.count docs.deleted store.size pri.store.size dataset.size
green open avis W9j_JrwJT4mdzpWcS5k7xg 1 0 609 0 74.6kb 74.6kb 74.6kb
green open .internal.alerts-security.alerts-default-000001 YJq3VGeRQKqD0fqJW6Fw3w 1 0 0 0 249b 249b 249b
green open acces aii68fsfQXKyt5wqkE1mPA 1 0 12000 0 1.5mb 1.5mb 1.5mb
green open cours pmq403ZgSZWeHJY9uNw1Qw 1 0 504 0 183.6kb 183.6kb 183.6kbΊδια απάντηση με το Ε1, συν μία γραμμή τίτλων από πάνω. Αυτό είναι το μόνο που κάνει το ?v. Τώρα δεν χρειάζεται πια να μαντεύεις: η στήλη docs.count είναι ο αριθμός των καρτών (609, 0, 12000, 504), η στήλη store.size είναι ο χώρος στον δίσκο.
Το ?v, τι σημαίνει ακριβώς;
? λέει: «αυτό που ακολουθεί είναι επιλογές». Χωρίζει την εντολή (_cat/indices) από τις ρυθμίσεις της.v είναι η επιλογή verbose, «φλύαρος»: «εμφάνισε επίσης τις κεφαλίδες των στηλών».Άρα ?v = «απάντησέ μου με τις κεφαλίδες, ώστε να καταλάβω τι διαβάζω». Απόκτησε τη συνήθεια να το βάζεις πάντα στις εντολές _cat. Χωρίς ?v, έχεις αριθμούς· με ?v, έχεις πληροφορία.
| Στήλη | Με απλά λόγια |
|---|---|
health | Υγεία του συρταριού. green: όλα καλά. yellow: οι κάρτες είναι εκεί, αλλά κάποια αντίγραφα ασφαλείας λείπουν. red: κάποιες κάρτες είναι απρόσιτες. |
status | open: χρησιμοποιήσιμο. close: κλειστό, δεν μπορείς ούτε να διαβάσεις ούτε να γράψεις. |
index | Το όνομα του συρταριού. |
uuid | Τεχνικός σειριακός αριθμός. Δεν τον χρησιμοποιούμε ποτέ με το χέρι. |
pri | Σε πόσα κομμάτια (primary shards) είναι χωρισμένο το συρτάρι. 1 στο εργαστήριο. |
rep | Πόσα αντίγραφα ασφαλείας (replicas) κάθε κομματιού. 0 στο εργαστήριο. |
docs.count | Αριθμός καρτών. Η στήλη που κοιτάμε πρώτη. |
docs.deleted | Κάρτες σημειωμένες «προς πέταγμα» που δεν έχουν ακόμα καθαριστεί από τον δίσκο. |
store.size | Συνολικός χώρος στον δίσκο. |
pri.store.size | Χώρος μόνο των κύριων κομματιών, χωρίς τα αντίγραφα. |
dataset.size | Μέγεθος των ίδιων των δεδομένων. |
Γιατί όλα είναι green στο εργαστήριο: το rep έχει τιμή 0, άρα δεν υπάρχει κανένα αντίγραφο να γίνει, άρα κανένα αντίγραφο δεν μπορεί να λείπει. Σε ένα πραγματικό cluster, rep 1 με ένα μόνο μηχάνημα θα έδινε yellow, επειδή το αντίγραφο δεν θα είχε πού να πάει.
GET _cat/indices/cours?vhealth status index uuid pri rep docs.count docs.deleted store.size pri.store.size dataset.size
green open cours pmq403ZgSZWeHJY9uNw1Qw 1 0 504 0 183.6kb 183.6kb 183.6kbΤι ζητά το ερώτημα: «Δείξε μου την ετικέτα του συρταριού cours.» Μία μόνο καινοτομία: το όνομα του συρταριού προστέθηκε μετά το _cat/indices/. Μία μόνο γραμμή σε απάντηση, αυτή του cours, με τις 504 κάρτες του. Ό,τι ακολουθεί το τελευταίο / λειτουργεί ως φίλτρο.
Κάνε το ίδιο για τα άλλα δύο συρτάρια:
GET _cat/indices/avis?v
GET _cat/indices/acces?v«Δείξε μου την ετικέτα του συρταριού των κριτικών»: 609 στο docs.count. «Δείξε μου την ετικέτα του συρταριού των αρχείων καταγραφής πρόσβασης»: 12000. Στο Dev Tools, όταν πολλά ερωτήματα είναι επικολλημένα στη σειρά, μόνο αυτό όπου βρίσκεται ο δρομέας στέλνεται: τοποθέτησέ τον στη σωστή γραμμή πριν το Ctrl+Enter.
GET _cat/indices/acces,avis,cours?v&s=index&h=index,health,docs.count,store.sizeindex health docs.count store.size
acces green 12000 1.5mb
avis green 609 74.6kb
cours green 504 183.6kbΤι ζητά το ερώτημα: «Δείξε μου έναν συνοπτικό πίνακα των συρταριών acces, avis και cours. Ταξινόμησέ τα κατά όνομα, και εμφάνισε μόνο το όνομά τους, την υγεία τους, τον αριθμό των καρτών τους και το μέγεθός τους.» Τέσσερις γραμμές, τέσσερις στήλες, ό,τι χρειάζεται και τίποτα παραπάνω. Είναι το ερώτημα που πρέπει να έχεις πρόχειρο για να επαληθεύεις το εργαστήριο με μια ματιά.
Φαίνεται περίπλοκο επειδή είναι μακρύ, αλλά είναι απλά το Ε3 με τρεις παραπάνω επιλογές. Ανάλυση, κομμάτι προς κομμάτι:
| Κομμάτι | Με απλά λόγια |
|---|---|
GET | Ζητάω μια πληροφορία, δεν τροποποιώ τίποτα. |
_cat/indices | Οι ετικέτες των συρταριών, σε ευανάγνωστο πίνακα. |
/acces,avis,cours | Μόνο αυτά τα τρία συρτάρια (ονόματα χωρισμένα με κόμματα, χωρίς κενό). Το εσωτερικό συρτάρι του Kibana εξαφανίζεται. |
?v | Με τις κεφαλίδες στηλών. |
&s=index | s όπως sort: ταξινομεί τις γραμμές κατά αλφαβητική σειρά της στήλης index. |
&h=index,health,docs.count,store.size | h όπως headers: εμφανίζει μόνο αυτές τις στήλες, με αυτή τη σειρά. |
Ένας κανόνας και μόνο ένας για τις επιλογές: η πρώτη ξεκινά με ?, όλες οι επόμενες με &. Γι' αυτό διαβάζουμε ?v&s=…&h=….
Και οι τέσσερις επιλεγμένες στήλες:
| Στήλη | Με απλά λόγια |
|---|---|
index | Το όνομα του συρταριού. |
health | Η υγεία του: green όλα λειτουργούν· yellow οι κάρτες υπάρχουν αλλά αντίγραφα ασφαλείας λείπουν· red κάποιες κάρτες είναι μη διαθέσιμες. |
docs.count | Πόσες κάρτες περιέχει. |
store.size | Πόσο χώρο πιάνει στον δίσκο. |
Με μία φράση: τα τρία σου συρτάρια είναι ανοιχτά, σε καλή υγεία, και περιέχουν ακριβώς τις αναμενόμενες κάρτες, 12.000, 609 και 504.
Χωρίς h=, το Elasticsearch επιστρέφει έντεκα στήλες, οι περισσότερες από τις οποίες δεν σου λένε τίποτα καθημερινά. Χωρίς s=, η σειρά των γραμμών είναι τυχαία και αλλάζει από κλήση σε κλήση (κοίτα το Ε2: το avis ερχόταν πριν το acces). Ονομάζοντας τα τρία συρτάρια, αποκλείεις επίσης αυτό του Kibana. Το αποτέλεσμα χωράει σε τέσσερις γραμμές και συγκρίνεται με μια ματιά με τους αναμενόμενους αριθμούς. Είναι ακριβώς αυτό που κάνει η εντολή etat του kit στα παρασκήνια.
Χρήσιμη παραλλαγή αν θέλεις να δεις επίσης ότι τα συρτάρια είναι ανοιχτά: πρόσθεσε status στη λίστα h=:
GET _cat/indices/acces,avis,cours?v&s=index&h=index,health,status,docs.count,store.sizeindex health status docs.count store.size
acces green open 12000 1.5mb
avis green open 609 74.6kb
cours green open 504 183.6kbΑπολογισμός Ε1 έως Ε4: ακόμη δεν έχεις διαβάσει καμία κάρτα. Κοίταξες μόνο τις ετικέτες στα συρτάρια. Ξέρεις ότι υπάρχουν 504 μαθήματα, αλλά δεν έχεις δει ακόμα ούτε έναν τίτλο μαθήματος. Αυτό κάνουμε τώρα.
GET cours/_count{
"count": 504,
"_shards": { "total": 1, "successful": 1, "skipped": 0, "failed": 0 }
}Τι ζητά το ερώτημα: «Μέτρησε τις κάρτες του συρταριού cours.» Απάντηση: 504. Κοίτα καλά τη μορφή του ερωτήματος, είναι καινούργια: δεν μιλάμε πια στο ντουλάπι (_cat/…), μιλάμε σε ένα συρτάρι. Το όνομα του συρταριού έρχεται πρώτο (cours), μετά τι θέλουμε να του κάνουμε (_count), χωρισμένα με /. Όλα τα ερωτήματα που ακολουθούν θα έχουν αυτή τη μορφή: όνομα-συρταριού/_ενέργεια.
Άλλη αλλαγή: η απάντηση δεν είναι πια πίνακας κειμένου αλλά JSON, με αγκύλες και εισαγωγικά. Είναι η κανονική μορφή απαντήσεων του Elasticsearch· το _cat ήταν η εξαίρεση. Αγνόησε το κομμάτι _shards, λέει μόνο «το κομμάτι του συρταριού απάντησε, τίποτα δεν απέτυχε».
Δοκίμασε GET avis/_count (609) και GET acces/_count (12000). Σε SQL: SELECT COUNT(*) FROM cours.
GET cours/_searchΤι ζητά το ερώτημα: «Άνοιξε το συρτάρι cours και δείξε μου τις κάρτες που είναι μέσα.» Είναι η πρώτη φορά που βλέπεις ένα πραγματικό μάθημα: τον τίτλο του, την τιμή του, τον καθηγητή του.
Η απάντηση είναι μακριά: είναι φυσιολογικό, περιέχει δέκα πλήρεις κάρτες. Το Elasticsearch σου δίνει μόνο τις 10 πρώτες, ακόμη κι αν το συρτάρι περιέχει 504· είναι μια προστασία, για να μην σου στείλει 504 κάρτες μεμιάς χωρίς να το έχεις ζητήσει. Κοίτα τη δομή αντί για το περιεχόμενο:
{
"took": 1,
"timed_out": false,
"_shards": { … },
"hits": {
"total": { "value": 504, "relation": "eq" },
"max_score": 1,
"hits": [
{
"_index": "cours",
"_id": "C0001",
"_score": 1,
"_source": {
"id": "C0001",
"titre": "Docker expliqué simplement",
"categorie": "DevOps",
"niveau": "debutant",
"prix": 129,
"tags": ["docker", "linux", "helm", "devops"],
"professeur": { "id": "P001", "nom": "Karim Caron", "ville": "Gatineau" },
…
}
},
… 9 autres documents …
]
}
}Το _search σημαίνει «ψάξε»· χωρίς άλλη διευκρίνιση, σημαίνει «δώσε μου κάρτες, οποιεσδήποτε». Οι δύο αριθμοί προς εντοπισμό: "total": { "value": 504 } (υπάρχουν 504 κάρτες στο συρτάρι) και η λίστα hits που περιέχει μόνο 10 (αυτές που σου δείχνει). Κάθε κάρτα είναι στο _source, με όλα τα κουτάκια της: titre, prix, categorie…
Κάνε το ίδιο με τα άλλα δύο συρτάρια: GET avis/_search («άνοιξε το συρτάρι avis, δείξε μου τις 10 πρώτες κριτικές») και GET acces/_search (τις 10 πρώτες γραμμές αρχείου καταγραφής). Σε SQL: SELECT * FROM cours LIMIT 10.
Η βασική διαφορά, να μην την μπερδέψεις ποτέ ξανά.
Το
GET _cat/indices/cours?vκοιτάζει την ετικέτα του συρταριού: την κατάστασή του, τον αριθμό των καρτών του, το μέγεθός του. Μία γραμμή. Δεν βλέπεις κανένα μάθημα.Το
GET cours/_searchανοίγει το συρτάρι και διαβάζει τις κάρτες: τίτλους, τιμές, κατηγορίες, καθηγητές. Δέκα κάρτες. Βλέπεις τα ίδια τα μαθήματα.Το πρώτο απαντά «υπάρχουν 504 μαθήματα». Το δεύτερο απαντά «ορίστε μαθήματα».
| Κλειδί | Με απλά λόγια |
|---|---|
took | Ο χρόνος που χρειάστηκε το Elasticsearch, σε χιλιοστά του δευτερολέπτου (εδώ 1 ms). |
hits.total.value | Ο συνολικός αριθμός καρτών που ταιριάζουν: 504. Ακόμη κι αν σου δείχνει μόνο 10, σου λέει πόσες υπάρχουν συνολικά. |
hits.hits | Η λίστα των καρτών που σου δείχνει: οι 10 πρώτες εξ ορισμού. |
_index | Το συρτάρι από όπου προέρχεται η κάρτα (cours). |
_id | Ο αριθμός γραμμένος πάνω στην κάρτα (C0001, C0002…). |
_score | Μια βαθμολογία συνάφειας. Είναι 1 παντού εδώ επειδή δεν αναζητήσαμε τίποτα συγκεκριμένο: όλες οι κάρτες αξίζουν το ίδιο. |
_source | Η ίδια η κάρτα, όπως τακτοποιήθηκε στο συρτάρι, με όλα τα κουτάκια της. |
Η λέξη hit σημαίνει «χτύπημα», όπως στη σκοποβολή: μια κάρτα «χτυπημένη» από το ερώτημα. Το πεδίο professeur είναι μια κάρτα μέσα στην κάρτα (ένα JSON μέσα στο JSON): ο καθηγητής είναι γραμμένος απευθείας πάνω στην κάρτα του μαθήματος, με το όνομά του και την πόλη του. Η SQL δεν το κάνει αυτό φυσικά, θα χρειαζόταν έναν δεύτερο πίνακα και ένα join.
GET cours/_search
{
"size": 20
}Τι ζητά το ερώτημα: «Άνοιξε το συρτάρι cours και δείξε μου 20 κάρτες αντί για τις συνηθισμένες 10.»
Μία μόνο καινοτομία, αλλά σημαντική: το ερώτημα έχει τώρα δύο μέρη. Η πρώτη γραμμή (GET cours/_search) λέει τι να κάνει· το μπλοκ μέσα σε αγκύλες από κάτω, που ονομάζουμε σώμα (body), δίνει διευκρινίσεις. Εδώ η διευκρίνιση είναι "size": 20: «μέγεθος παρτίδας = 20». Αυτό είναι όλο. Το size σημαίνει απλά «πόσες κάρτες θέλεις να σου δείξουμε».
Επαλήθευσε στην απάντηση: το hits.total.value παραμένει 504 (το συρτάρι δεν άλλαξε), αλλά η λίστα hits περιέχει τώρα 20 κάρτες αντί για 10. Σε SQL: LIMIT 20.
Στην κλασική HTTP, το GET δεν έχει σώμα· το Elasticsearch το δέχεται παρόλα αυτά επειδή είναι πρακτικό στο Dev Tools. Αν ένα εργαλείο αρνηθεί, το POST cours/_search με το ίδιο σώμα κάνει ακριβώς το ίδιο πράγμα. Άλλο όριο που πρέπει να ξέρεις: το size δεν μπορεί να ξεπεράσει τα 10.000 μεμιάς (παράμετρος index.max_result_window)· για να διατρέξεις περισσότερα, κάνεις σελιδοποίηση. Για τα 504 μαθήματά μας, το "size": 504 θα λειτουργούσε, αλλά η απάντηση θα είχε χιλιάδες γραμμές: δεν είναι έτσι που διαβάζουμε δεδομένα, οι συγκεντρώσεις (E13) είναι φτιαγμένες γι' αυτό.
GET cours/_search
{
"size": 3,
"_source": ["titre", "prix"]
}"hits": [
{ "_id": "C0001", "_source": { "titre": "Docker expliqué simplement", "prix": 129 } },
{ "_id": "C0002", "_source": { "titre": "Docker avancé : industrialiser des conteneurs en production", "prix": 29 } },
{ "_id": "C0003", "_source": { "titre": "Docker : le guide complet", "prix": 19 } }
]Τι ζητά το ερώτημα: «Δείξε μου 3 κάρτες, αλλά μόνο τα κουτάκια titre και prix κάθε κάρτας.» Καινοτομία: _source με μια λίστα πεδίων. Αντί για ολόκληρη την κάρτα, κρατάμε μόνο τα κουτάκια που μας ενδιαφέρουν. Η απάντηση γίνεται ευανάγνωστη με μια ματιά. Σε SQL: SELECT titre, prix FROM cours LIMIT 3. Θα χρησιμοποιήσουμε το _source σε σχεδόν όλα τα επόμενα ερωτήματα, ακριβώς για να κρατάμε σύντομες απαντήσεις.
GET cours/_doc/C0001{
"_index": "cours",
"_id": "C0001",
"_version": 6,
"found": true,
"_source": {
"id": "C0001",
"titre": "Docker expliqué simplement",
"categorie": "DevOps",
"sujet": "Docker",
"niveau": "debutant",
"prix": 129,
"note_moyenne": 4.4,
"nb_avis": 327,
"professeur": { "id": "P001", "nom": "Karim Caron", "ville": "Gatineau" },
…
}
}Καμία αναζήτηση εδώ: ζητάμε το έγγραφο του οποίου το αναγνωριστικό είναι C0001, και το Elasticsearch το επιστρέφει απευθείας ("found": true). Είναι η ταχύτερη πρόσβαση που υπάρχει. Σε SQL: SELECT * FROM cours WHERE id = 'C0001'. Κράτα αυτό το μάθημα, «Docker expliqué simplement», του Karim Caron: θα το ξαναβρούμε στο Neo4j σε λίγο, για να αποδείξουμε ότι οι δύο κινητήρες περιέχουν τα ίδια δεδομένα.
Το _id (με κάτω παύλα) είναι το τεχνικό αναγνωριστικό του εγγράφου στο Elasticsearch· το id (χωρίς παύλα) είναι ένα απλό πεδίο μέσα στο _source. Το kit τα έκανε σκόπιμα ίδια (C0001 και στις δύο πλευρές) για να είναι ευανάγνωστο. Αυτό είναι επίσης που κάνει το importer επαναλήψιμο: η αποστολή δύο φορές ενός εγγράφου με το ίδιο _id αντικαθιστά το πρώτο αντί να δημιουργήσει ένα δεύτερο, εξ ου και το _version: 6 (το έγγραφο ξαναγράφτηκε έξι φορές στο μηχάνημα του μαθήματος, χωρίς ποτέ να διπλασιαστεί).
GET cours/_search
{
"query": { "match": { "titre": "kubernetes" } },
"_source": ["titre"]
}"hits": {
"total": { "value": 7, "relation": "eq" },
"hits": [
{ "_score": 5.0897474, "_source": { "titre": "Les bases de Kubernetes" } },
{ "_score": 5.0897474, "_source": { "titre": "Kubernetes en pratique" } },
{ "_score": 5.0897474, "_source": { "titre": "Kubernetes pour les débutants" } },
…
]
}Καινοτομία: query, το κομμάτι του σώματος που λέει τι να αναζητήσουμε. Το match είναι η βασική αναζήτηση: «τα έγγραφα των οποίων το πεδίο titre περιέχει τη λέξη kubernetes». Επτά μαθήματα ταιριάζουν, και για πρώτη φορά το _score δεν είναι πια 1: είναι η συνάφεια, και τα αποτελέσματα ταξινομούνται από το πιο συναφές στο λιγότερο συναφές. Σε SQL, το κατά προσέγγιση ισοδύναμο θα ήταν WHERE titre LIKE '%kubernetes%', αλλά κοίτα καλά: πληκτρολογήσαμε kubernetes με πεζά και βρίσκουμε «Kubernetes» με κεφαλαίο. Το LIKE δεν θα το είχε κάνει αυτό.
Τη στιγμή που ένα μάθημα ευρετηριάζεται, το Elasticsearch κόβει τον τίτλο του σε λέξεις, τις μετατρέπει σε πεζά, αφαιρεί τους τόνους και επαναφέρει κάθε λέξη στη ρίζα της (το «conteneurs» γίνεται «conteneur»): αυτή είναι η ανάλυση, εδώ γίνεται από τον αναλυτή french που ορίζεται στο mapping του kit. Όταν αναζητάς, το ερώτημά σου υφίσταται την ίδια επεξεργασία, μετά το Elasticsearch συγκρίνει λέξη προς λέξη. Αποτέλεσμα: κεφαλαία, τόνοι και πληθυντικοί δεν μετράνε πια. Το _score ανεβαίνει όταν η λέξη είναι σπάνια στο index και συχνή στο έγγραφο. Η ενότητα 3 αφιερώνει πολλά μαθήματα σε αυτό· εδώ, κράτα μόνο: το match αναζητά λέξεις, όχι ακολουθίες χαρακτήρων.
Πρώτα η απόδειξη ότι, εξ ορισμού, ένα πραγματικό λάθος δεν βρίσκει τίποτα. «kubrenetes» (δύο γράμματα αντεστραμμένα):
GET cours/_count
{
"query": { "match": { "titre": "kubrenetes" } }
}{ "count": 0, … }Μετά το ίδιο ερώτημα με μια μόνο γραμμή παραπάνω, fuzziness, που επιτρέπει μία ή δύο διαφορές γραμμάτων:
GET cours/_search
{
"query": { "match": { "titre": { "query": "kubrenetes", "fuzziness": "AUTO" } } },
"size": 3,
"_source": ["titre", "niveau", "prix"]
}"total": { "value": 7, "relation": "eq" },
"hits": [
{ "_score": 4.4535294, "_source": { "titre": "Les bases de Kubernetes", "niveau": "debutant", "prix": 0 } },
{ "_score": 4.4535294, "_source": { "titre": "Kubernetes en pratique", "niveau": "debutant", "prix": 129 } },
{ "_score": 4.4535294, "_source": { "titre": "Kubernetes pour les débutants", "niveau": "debutant", "prix": 29 } }
]Τα 7 μαθήματα Kubernetes επιστρέφουν, παρά το λάθος. Είναι η στιγμή που πάντα εκπλήσσει στην αίθουσα, και είναι ο λόγος ύπαρξης του Elasticsearch σε μια γραμμή αναζήτησης: ο χρήστης πληκτρολογεί λάθος, ο κινητήρας καταλαβαίνει παρόλα αυτά. Ένα LIKE '%kubrenetes%' σε SQL δεν θα είχε επιστρέψει ποτέ τίποτα. Παρατήρησε τη σύνταξη: όταν το match χρειάζεται επιλογές, η τιμή του πεδίου γίνεται αντικείμενο { "query": …, "fuzziness": … } αντί για απλή αλυσίδα χαρακτήρων.
Το fuzziness μετρά τις τροποποιήσεις (ένα γράμμα προστέθηκε, αφαιρέθηκε, άλλαξε ή αντάλλαξε θέση με τον γείτονά του) που ανεχόμαστε μεταξύ της λέξης που πληκτρολογήθηκε και της λέξης που ευρετηριάστηκε. Το AUTO προσαρμόζει την ανοχή στο μήκος της λέξης: 0 λάθη για λέξη 1 ή 2 γραμμάτων, 1 λάθος για 3 έως 5 γράμματα, 2 λάθη πέρα από αυτό. Το «kubrenetes» έχει 10 γράμματα, άρα 2 λάθη επιτρέπονται· έχει μόνο ένα (η αντιμετάθεση re ↔ er): βρέθηκε. Το _score είναι λίγο χαμηλότερο απ' ό,τι στο Ε10 (4,45 έναντι 5,09): το Elasticsearch τιμωρεί ελαφρώς τις κατά προσέγγιση αντιστοιχίες, κάτι που κρατά τις ακριβείς αντιστοιχίες μπροστά.
GET cours/_search
{
"query": { "range": { "nb_avis": { "gte": 10 } } },
"sort": [{ "note_moyenne": "desc" }, { "nb_avis": "desc" }],
"size": 5,
"_source": ["titre", "note_moyenne", "nb_avis", "categorie"]
}Δύο καινοτομίες, που γίνονται κατανοητές στην ανάγνωση. range με gte (greater than or equal): μόνο τα μαθήματα που έχουν τουλάχιστον 10 κριτικές, για να αποκλείσουμε βαθμολογίες βασισμένες σε μία μόνο ψήφο. sort: ταξινόμηση κατά note_moyenne φθίνουσα, μετά κατά nb_avis φθίνουσα για να ξεχωρίσουν οι ισοπαλίες. Σε SQL: WHERE nb_avis >= 10 ORDER BY note_moyenne DESC, nb_avis DESC LIMIT 5. Όταν ταξινομούμε εμείς οι ίδιοι, το _score γίνεται null: η συνάφεια δεν χρησιμεύει πια, μετράει η δική μας σειρά.
GET cours/_search
{
"size": 0,
"aggs": {
"par_categorie": { "terms": { "field": "categorie" } }
}
}"aggregations": {
"par_categorie": {
"buckets": [
{ "key": "Cloud", "doc_count": 84 },
{ "key": "DevOps", "doc_count": 84 },
{ "key": "Données", "doc_count": 84 },
{ "key": "Développement web", "doc_count": 84 },
{ "key": "IA", "doc_count": 84 },
{ "key": "Sécurité", "doc_count": 84 }
]
}
}Καινοτομία: aggs (συγκεντρώσεις). Το terms στο categorie φτιάχνει ένα πακέτο (bucket) ανά διακριτή τιμή και μετρά τα έγγραφα μέσα. Το size: 0 λέει «μη μου επιστρέψεις κανένα έγγραφο, μόνο τους μετρητές»: η απάντηση είναι μικροσκοπική και στιγμιαία, ακόμη και σε εκατομμύρια γραμμές. Σε SQL: SELECT categorie, COUNT(*) FROM cours GROUP BY categorie. Αποτέλεσμα: έξι κατηγορίες με 84 μαθήματα η καθεμία. Το par_categorie είναι ένα όνομα που επιλέγεις εσύ· χρησιμεύει μόνο για να ξαναβρείς το αποτέλεσμα στην απάντηση.
Κάθε γράφημα του Kibana, κάθε πίτα, κάθε ιστόγραμμα είναι μια συγκέντρωση σαν αυτή, εκτελεσμένη από το Elasticsearch και σχεδιασμένη από το Kibana. Όταν θα κατασκευάσεις ένα ταμπλό στην ενότητα 4, θα κάνεις κλικ σε «πεδίο: categorie, συγκέντρωση: terms» και το Kibana θα στείλει ακριβώς αυτό το ερώτημα. Το να ξέρεις να το διαβάζεις σημαίνει να ξέρεις τι κάνει το ταμπλό στα παρασκήνια.
GET cours/_search
{
"size": 0,
"aggs": {
"par_categorie": {
"terms": { "field": "categorie" },
"aggs": { "par_niveau": { "terms": { "field": "niveau" } } }
}
}
}Ίδιο ερώτημα με το Ε13, με ένα δεύτερο aggs μέσα στο πρώτο. Μέσα σε κάθε πακέτο κατηγορίας, ξαναφτιάχνουμε πακέτα κατά niveau. Αποτέλεσμα: για το «Cloud», 45 αρχάριος, 28 προχωρημένος, 11 ενδιάμεσος· και ούτω καθεξής για τις έξι κατηγορίες, σε ένα μόνο πέρασμα. Σε SQL: GROUP BY categorie, niveau, αλλά η απάντηση είναι ήδη ιεραρχημένη, έτοιμη για ένα σωρευτικό γράφημα.
GET acces/_search
{
"size": 0,
"query": { "range": { "statut": { "gte": 500 } } },
"aggs": { "codes": { "terms": { "field": "statut" } } }
}"hits": { "total": { "value": 178, "relation": "eq" } },
"aggregations": {
"codes": {
"buckets": [
{ "key": 500, "doc_count": 122 },
{ "key": 503, "doc_count": 56 }
]
}
}Τίποτα καινούργιο: συνδυάζουμε range (E12) και terms (E13) στο index acces. Από τις 12.000 γραμμές αρχείου καταγραφής, 178 έχουν κωδικό HTTP ≥ 500: 122 σφάλματα 500 και 56 503. Είναι η ερώτηση που θέτει κάθε πρωί ένας υπεύθυνος πλατφόρμας, και απαντάται σε ένα χιλιοστό του δευτερολέπτου. Σε SQL: SELECT statut, COUNT(*) FROM acces WHERE statut >= 500 GROUP BY statut. Είναι ακριβώς αυτό που θα βάλεις σε ένα ταμπλό στην ενότητα 4.
Άνοιξε http://localhost:7474. Οθόνη σύνδεσης: URL neo4j://localhost:7687, χρήστης neo4j, κωδικός aiopsatlas2026. Πάνω, μια γραμμή επεξεργασίας που ξεκινά με neo4j$: επικόλλησε ένα μόνο ερώτημα, μετά Ctrl+Enter ή το τρίγωνο ▶. Το αποτέλεσμα εμφανίζεται σε ένα πλαίσιο από κάτω, με καρτέλες αριστερά: Graph (ένα σχέδιο, όταν το αποτέλεσμα περιέχει κόμβους), Table (γραμμές και στήλες) και Text.
MATCH (n) RETURN nΤο αποτέλεσμα είναι ένα σύννεφο έγχρωμων φυσαλίδων, συνδεδεμένων με βέλη, που μπορείς να μετακινήσεις με το ποντίκι. Το Neo4j Browser εμφανίζει το πολύ 300 κόμβους ταυτόχρονα (ένα μήνυμα πάνω από το αποτέλεσμα το επισημαίνει)· οι 872 είναι όλοι εκεί, απλά σχεδιάζει μόνο ένα μέρος για να παραμείνει ευανάγνωστο.
Είναι το απλούστερο ερώτημα της Cypher, της γλώσσας του Neo4j. Το MATCH σημαίνει «βρες», το (n) δηλώνει έναν οποιονδήποτε κόμβο (οι παρενθέσεις σχεδιάζουν έναν κύκλο, σαν φυσαλίδα) στον οποίο δίνουμε το όνομα n, και το RETURN n σημαίνει «δείξε μου τον». Σε SQL, δεν υπάρχει ισοδύναμο: θα ήταν «SELECT * όλων των πινάκων ταυτόχρονα», κάτι που η SQL δεν ξέρει να κάνει.
Ένας γράφος αποτελείται από δύο πράγματα: κόμβους (τις φυσαλίδες) και σχέσεις (τα βέλη μεταξύ φυσαλίδων). Κάθε κόμβος φέρει μια ετικέτα που λέει τι είναι (Cours, Etudiant…) και ιδιότητες (τίτλος, τιμή…). Κάθε σχέση φέρει έναν τύπο (INSCRIT_A, ENSEIGNE…) και μια κατεύθυνση.
| Neo4j | SQL |
|---|---|
Ετικέτα Cours | Πίνακας cours |
| Κόμβος | Γραμμή |
| Ιδιότητα | Στήλη |
Σχέση INSCRIT_A | Πίνακας σύνδεσης inscriptions + joins |
Ο γράφος του εργαστηρίου:
Neo4j
├── 504 nœuds Cours
├── 300 nœuds Etudiant
├── 30 nœuds Professeur
├── 22 nœuds Competence
└── 16 nœuds Ville
= 872 nœuds, reliés par 3 712 relationsΤα 504 μαθήματα είναι τα ίδια με τα 504 έγγραφα του index Elasticsearch cours: είναι δύο κινητήρες που τακτοποιούν τα ίδια δεδομένα με δύο τρόπους, ο καθένας για να απαντά σε διαφορετικές ερωτήσεις.
MATCH (n) RETURN n LIMIT 25Μία μόνο καινοτομία: LIMIT 25, ίδια λέξη με SQL. Είκοσι πέντε φυσαλίδες αντί για τριακόσιες: επιτέλους βλέπουμε κάτι. Πέρασε το ποντίκι πάνω από μια φυσαλίδα: οι ιδιότητές της εμφανίζονται στο κάτω μέρος του πλαισίου. Κάνε διπλό κλικ πάνω της: οι γείτονές της ξεδιπλώνονται.
MATCH (n) RETURN count(n) AS totaltotal
872Το count(n) μετρά αντί να σχεδιάζει· το AS total ονομάζει τη στήλη. Κανένα σχέδιο πια: το αποτέλεσμα περνά μόνο του σε προβολή Table, αφού ένας αριθμός δεν σχεδιάζεται. 872, ο αριθμός του etat. Σε SQL: SELECT COUNT(*).
MATCH (c:Cours) RETURN c LIMIT 5Καινοτομία: :Cours μετά το όνομα μεταβλητής. Είναι η ετικέτα: «μόνο οι κόμβοι που είναι μαθήματα». Πέντε φυσαλίδες, όλες του ίδιου χρώματος. Σε SQL: SELECT * FROM cours LIMIT 5. Από συνήθεια, ονομάζουμε τη μεταβλητή με το αρχικό της ετικέτας (c για Cours, e για Etudiant), αλλά και το n θα λειτουργούσε.
MATCH (c:Cours) RETURN c.titre, c.prix LIMIT 5c.titre c.prix
"Docker expliqué simplement" 129.0
"Docker avancé : industrialiser des conteneurs en production" 29.0
"Docker : le guide complet" 19.0
…Καινοτομία: c.titre, c.prix. Η τελεία δίνει πρόσβαση σε μια ιδιότητα του κόμβου. Όταν επιστρέφουμε ιδιότητες αντί για ολόκληρους κόμβους, το Neo4j Browser περνά σε προβολή Table. Σε SQL: SELECT titre, prix FROM cours LIMIT 5. Σύγκρινε με το E8: ίδιοι τίτλοι, ίδιες τιμές, ίδια σειρά. Για να μάθεις όλες τις ιδιότητες ενός μαθήματος: το MATCH (c:Cours) RETURN keys(c) LIMIT 1 απαντά prix, duree_heures, date_publication, sujet, niveau, categorie, id, titre.
MATCH (n) RETURN labels(n)[0] AS type, count(*) AS nombre ORDER BY nombre DESCtype nombre
"Cours" 504
"Etudiant" 300
"Professeur" 30
"Competence" 22
"Ville" 16Το labels(n) επιστρέφει τη λίστα ετικετών του κόμβου (ένας κόμβος μπορεί να έχει πολλές· εδώ μία μόνο, εξ ου και το [0], το πρώτο στοιχείο). Το count(*) ομαδοποιεί αυτόματα με ό,τι δεν είναι συγκέντρωση: δεν χρειάζεται να γράψεις GROUP BY, η Cypher το συνάγει. Το ORDER BY nombre DESC ταξινομεί. Το άθροισμα των πέντε γραμμών κάνει 872. Σε SQL, θα χρειάζονταν πέντε SELECT COUNT(*) και UNION.
MATCH (c:Cours {id: 'C0001'}) RETURN c.titre, c.prix, c.niveauc.titre c.prix c.niveau
"Docker expliqué simplement" 129.0 "debutant"Καινοτομία: οι αγκύλες {id: 'C0001'} μέσα στο μοτίβο. Φιλτράρουν μια ιδιότητα, όπως ένα WHERE id = 'C0001'. Και είναι το ίδιο μάθημα με το E9 στο Elasticsearch: ίδιος τίτλος, ίδια τιμή. Να η απόδειξη ότι οι δύο κινητήρες περιέχουν πράγματι τα ίδια δεδομένα· αυτό που αλλάζει είναι τι μπορούμε να τους ζητήσουμε.
MATCH (c:Cours) WHERE c.titre CONTAINS 'Kubernetes' RETURN c.titre ORDER BY c.titrec.titre
"Kubernetes : de zéro à la production"
"Kubernetes : le guide complet"
"Kubernetes avancé : superviser des conteneurs en production"
"Kubernetes en pratique"
"Kubernetes expliqué simplement"
"Kubernetes pour les débutants"
"Les bases de Kubernetes"Το WHERE γράφεται όπως σε SQL και το CONTAINS αναζητά μια ακολουθία χαρακτήρων. Επτά μαθήματα, τα ίδια με το Ε10. Αλλά δοκίμασε CONTAINS 'kubernetes' με πεζά: μηδέν αποτελέσματα. Και CONTAINS 'kubrenetes': μηδέν επίσης. Το Neo4j συγκρίνει χαρακτήρες, με ακρίβεια· δεν γνωρίζει ούτε πεζά/κεφαλαία, ούτε λέξεις, ούτε λάθη. Ακριβώς γι' αυτό το εργαστήριο έχει τους δύο κινητήρες: η γραμμή αναζήτησης είναι το Elasticsearch· οι σύνδεσμοι μεταξύ πραγμάτων είναι το Neo4j.
MATCH (c:Cours) RETURN c.titre, c.prix ORDER BY c.prix DESC LIMIT 5c.titre c.prix
"Helm par la pratique : superviser un pipeline CI/CD" 199.0
"Atelier GitHub Actions : vos applications" 199.0
"Docker pour les débutants" 199.0
…Τίποτα καινούργιο: ORDER BY … DESC LIMIT 5, όπως σε SQL. Τα πέντε πιο ακριβά μαθήματα, όλα στα 199 $.
MATCH (c:Cours) RETURN c.categorie AS categorie, count(*) AS nombre ORDER BY nombre DESCcategorie nombre
"DevOps" 84
"Données" 84
"IA" 84
"Développement web" 84
"Sécurité" 84
"Cloud" 84Ακριβώς το αποτέλεσμα της συγκέντρωσης Ε13 στο Elasticsearch: έξι κατηγορίες των 84. Ίδιο δεδομένο, δύο κινητήρες, δύο συντάξεις. Μέχρι εδώ, το Neo4j δεν έκανε τίποτα που η SQL δεν ξέρει να κάνει. Αυτό αλλάζει στο επόμενο ερώτημα.
MATCH (p:Professeur)-[r:ENSEIGNE]->(c:Cours) RETURN p, r, c LIMIT 30Πέρνα σε προβολή Graph: φυσαλίδες «καθηγητής» συνδεδεμένες με βέλη ENSEIGNE σε φυσαλίδες «μάθημα». Πιάσε έναν καθηγητή με το ποντίκι, βλέπεις όλα τα μαθήματά του να ακολουθούν.
Καινοτομία: το βέλος. Το (p:Professeur)-[r:ENSEIGNE]->(c:Cours) διαβάζεται κυριολεκτικά «ένας καθηγητής, που διδάσκει, ένα μάθημα». Οι παρενθέσεις είναι κόμβοι, οι αγκύλες είναι η σχέση, το -> δίνει την κατεύθυνση. Είναι ένα σχέδιο ASCII αυτού που ψάχνουμε, και το Neo4j βρίσκει όλα τα σημεία του γράφου που μοιάζουν με αυτό το σχέδιο. Σε SQL, θα ήταν SELECT * FROM professeurs JOIN cours ON cours.professeur_id = professeurs.id, και δεν θα σχεδιαζόταν.
Σε SQL, μια σχέση μεταξύ δύο γραμμών δεν υπάρχει πραγματικά: επανυπολογίζεται σε κάθε ερώτημα με ένα join, που συγκρίνει αναγνωριστικά. Με δέκα joins αλυσιδωτά, γίνεται αργό και δυσανάγνωστο. Στο Neo4j, η σχέση αποθηκεύεται ως φυσικό βέλος μεταξύ δύο κόμβων: η διάτρεξη ενός βέλους κοστίζει το ίδιο ανεξάρτητα από τον αριθμό κόμβων στη βάση. Αυτό είναι που κάνει δυνατά τα ερωτήματα N14 έως N16, που αλυσιδώνουν πολλά άλματα χωρίς κόπο.
MATCH ()-[r]->() RETURN type(r) AS relation, count(*) AS nombre ORDER BY nombre DESCrelation nombre
"INSCRIT_A" 1654
"COUVRE" 994
"ENSEIGNE" 504
"HABITE" 330
"PREREQUIS_DE" 230Το () είναι ένας οποιοσδήποτε κόμβος του οποίου δεν κρατάμε καν το όνομα· το [r] μια σχέση οποιουδήποτε τύπου· το type(r) ο τύπος της. Πέντε τύποι σχέσεων, 3.712 συνολικά: ο αριθμός που εμφανίζει το πλαίσιο Database information του Neo4j Browser. Διαβάζουμε το μοντέλο του εργαστηρίου σε μία γραμμή: μαθητές εγγεγραμμένοι σε μαθήματα, μαθήματα που καλύπτουν δεξιότητες, καθηγητές που διδάσκουν μαθήματα, άνθρωποι που κατοικούν σε πόλεις, και μαθήματα προαπαιτούμενα άλλων μαθημάτων. Το ENSEIGNE στο 504 λέει ότι υπάρχει ακριβώς μία σχέση ανά μάθημα: κάθε μάθημα έχει έναν και μοναδικό καθηγητή.
MATCH (p:Professeur {id: 'P001'})-[:ENSEIGNE]->(c:Cours)
RETURN p.prenom + ' ' + p.nom AS professeur, count(c) AS nb_coursprofesseur nb_cours
"Karim Caron" 11Συνδυάζουμε το N7 (το φίλτρο {id: 'P001'}) και το N11 (το βέλος). Το P001 είναι ο καθηγητής του «Docker expliqué simplement» που είδαμε στο E9 και N7· διδάσκει 11 μαθήματα. Το + κολλάει αλυσίδες χαρακτήρων, όπως σε SQL με το ||. Παρατήρησε το [:ENSEIGNE] χωρίς όνομα μεταβλητής: όταν δεν χρειαζόμαστε τη σχέση στο RETURN, δεν την ονομάζουμε.
MATCH (debut:Cours {id: 'C0111'}), (fin:Cours {id: 'C0110'}),
chemin = shortestPath((debut)-[:PREREQUIS_DE*]-(fin))
RETURN length(chemin) AS sauts, [n IN nodes(chemin) | n.titre] AS parcourssauts parcours
5 ["Neo4j : de zéro à la production", "Neo4j en pratique", "Neo4j par la pratique : interroger des journaux applicatifs", "Neo4j avancé : modéliser des journaux applicatifs", "Neo4j : le guide complet", "Maîtriser Neo4j"]Πρώτο ερώτημα που δεν μοιάζει πια με τίποτα σε SQL. Το [:PREREQUIS_DE*] με τον αστερίσκο σημαίνει «ακολουθώντας αυτή τη σχέση όσες φορές χρειάζεται». Το shortestPath ζητά τη συντομότερη από τις δυνατές διαδρομές μεταξύ των δύο μαθημάτων. Η απάντηση: 5 άλματα, και η λίστα των έξι τίτλων προς ακολούθηση, με τη σειρά, για να πάμε από το πρώτο στο τελευταίο μάθημα της σειράς Neo4j. Είναι ένα πλάνο εκπαίδευσης που υπολογίζεται εν κινήσει. Σε SQL, θα χρειαζόταν ένα αναδρομικό ερώτημα δεκάδων γραμμών, και θα ήταν αργό.
Το nodes(chemin) δίνει τη λίστα των διατρεχθέντων κόμβων. Το [n IN nodes(chemin) | n.titre] διαβάζεται «για κάθε κόμβο n αυτής της λίστας, κράτα τον τίτλο του»: είναι ένας συνοπτικός τρόπος μετατροπής μιας λίστας κόμβων σε λίστα τίτλων. Το length(chemin) μετρά τις διατρεχθείσες σχέσεις (5 σχέσεις για 6 κόμβους). Η ενότητα 6 αναλύει αυτές τις συναρτήσεις· εδώ το σημαντικό είναι το αποτέλεσμα: το Neo4j βρήκε ένα δρομολόγιο μέσα στον γράφο.
MATCH (x:Cours {id: 'C0213'})<-[:INSCRIT_A]-(e:Etudiant)-[:INSCRIT_A]->(autre:Cours)
WHERE autre <> x
RETURN autre.titre AS recommandation, autre.categorie AS categorie,
count(DISTINCT e) AS etudiants_communs
ORDER BY etudiants_communs DESC, recommandation
LIMIT 5recommandation categorie etudiants_communs
"Agents IA par la pratique : évaluer un pipeline de prédiction" "IA" 3
"Atelier NLP : des agents autonomes" "IA" 2
"Deep learning par la pratique : orchestrer un assistant …" "IA" 2
…Ο κινητήρας συστάσεων ενός εμπορικού ιστότοπου, σε πέντε γραμμές. Διάβασε το μοτίβο από αριστερά προς τα δεξιά: ξεκινάμε από το μάθημα x, ανεβαίνουμε το βέλος INSCRIT_A (δείχνει προς το x, εξ ου και το <-) μέχρι τους μαθητές e που το παρακολουθούν, μετά κατεβαίνουμε ένα άλλο βέλος INSCRIT_A προς τα άλλα μαθήματα autre αυτών των ίδιων μαθητών. Το WHERE autre <> x αποκλείει το ίδιο το αρχικό μάθημα. Το count(DISTINCT e) μετρά τους κοινούς μαθητές, χωρίς διπλότυπο. Τα μαθήματα στην κορυφή είναι αυτά που οι μαθητές του C0213 παρακολουθούν πιο συχνά παράλληλα: αυτές είναι οι συστάσεις. Σε SQL: δύο joins στον πίνακα εγγραφών, ένα GROUP BY, και ένα ερώτημα που το ξαναδιαβάζεις τρεις φορές πριν το καταλάβεις.
MATCH chemin = (x:Cours {id: 'C0213'})<-[:INSCRIT_A]-(:Etudiant)-[:INSCRIT_A]->(autre:Cours)
WHERE autre <> x
RETURN chemin LIMIT 50Πέρνα σε προβολή Graph: το αρχικό μάθημα στο κέντρο, οι μαθητές του γύρω, και τα μαθήματα που μοιράζονται στην περιφέρεια. Είναι το ερώτημα N15 χωρίς τη μέτρηση: επιστρέφουμε τις διαδρομές ολόκληρες αντί για στήλες, και το Neo4j Browser τις σχεδιάζει. Είναι η εικόνα που δείχνεις όταν κάποιος ρωτά «σε τι χρησιμεύει μια βάση γράφου».
Το μήνυμα προς μετάδοση. Δεκαπέντε ερωτήματα Elasticsearch, δεκαέξι ερωτήματα Cypher, και είδαμε, με τη σειρά: απαρίθμηση, μέτρηση, εμφάνιση, φιλτράρισμα, ταξινόμηση, ομαδοποίηση, ό,τι κάνει επίσης η SQL· μετά συγχώρεση ενός ορθογραφικού λάθους, συγκέντρωση 12.000 γραμμών σε ένα χιλιοστό του δευτερολέπτου, υπολογισμός συντομότερης διαδρομής και παραγωγή συστάσεων, ό,τι η SQL κάνει άσχημα ή καθόλου. Το ίδιο μάθημα «Docker expliqué simplement» εμφανίστηκε και στους δύο κινητήρες: ίδια δεδομένα, διαφορετικές ερωτήσεις. Ακριβώς γι' αυτό υπάρχει αυτό το εργαστήριο, και όλη η υπόλοιπη ύλη του μαθήματος αναλύει πώς λειτουργεί καθένα από αυτά τα ερωτήματα.
Ξανακάνε το βήμα 7 του παραρτήματός σου με docker compose stop neo4j: τι γίνεται με τη γραμμή Neo4j répond — nœuds : 872 στο etat; Τι λέει το Neo4j Browser, ήδη συνδεδεμένο, όταν ξανατρέχεις το ερώτημα N3 (MATCH (n) RETURN count(n)); Ποια είναι η τελευταία γραμμή του journal neo4j; Ξανάτρεξε με docker compose start neo4j, επαλήθευσε ότι οι 872 κόμβοι είναι ακόμα εκεί χωρίς να ξαναφορτώσεις τίποτα, και σημείωσε ποια από τις δύο υπηρεσίες επανεκκινεί πιο γρήγορα.
Όλες οι εντολές αυτού του παραρτήματος πληκτρολογούνται σε PowerShell (Windows Terminal, ή PowerShell 7), με .\labo.ps1 …. Οι έξοδοι που αναπαράγονται είναι αυτές του μηχανήματος του μαθήματος, σε Windows 11 και Docker Desktop.
docker-compose.yml και το labo.ps1)..\labo.ps1 («η εκτέλεση σεναρίων είναι απενεργοποιημένη σε αυτό το σύστημα»): Set-ExecutionPolicy -Scope CurrentUser RemoteSigned, απάντησε O, ξανατρέξε..\labo.ps1 etat πρέπει να εμφανίζει (healthy) παντού, "status":"green", acces 12000 avis 609 cours 504 και nœuds : 872.elasticsearch/requetes/01-pratique-demarrer-verifier-reparer.txt. Κανένα δεν γράφει στο cluster.Πριν ανάψεις οτιδήποτε, κάνε το σενάριο να μιλήσει.
.\labo.ps1 prerequisΣημείο ελέγχου: μόνο πράσινα τικ και η τελική φράση.
== Prérequis ==
✔ docker : Docker version 29.3.1, build c2be9cc
✔ le démon Docker répond
✔ docker compose : 5.1.1
✔ mémoire disponible pour Docker : 31 Go
✔ processeurs : 20
✔ port 9200 : utilisé par le labo lui-même
✔ port 5601 : utilisé par le labo lui-même
✔ port 7474 : utilisé par le labo lui-même
✔ port 7687 : utilisé par le labo lui-même
Tout est prêt. Lancez : .\labo.ps1 demarrerΣτο δικό σου μηχάνημα, εκδόσεις και μνήμη διαφέρουν, και αν το εργαστήριο δεν έχει τρέξει ποτέ οι τέσσερις τελευταίες γραμμές λένε port 9200 : libre. Και οι δύο αναγνώσεις είναι καλές.
Αν βλέπεις κάτι άλλο: ένας σταυρός ✘ περιέχει τη λύση του μέσα στη φράση (Docker Desktop όχι εκκινημένο, μνήμη κάτω από 4 GB, θύρα κατειλημμένη)· διόρθωσε, ξανατρέξε, προχώρα μόνο με την πράσινη γραμμή. Για τη μνήμη: Docker Desktop → Settings → Resources, ή το αρχείο %UserProfile%\.wslconfig αν το Docker Desktop χρησιμοποιεί WSL 2.
Ξεκίνα την εκκίνηση και, αυτή τη φορά, διάβασε ό,τι κυλά αντί να περιμένεις το τέλος.
.\labo.ps1 demarrerΣημείο ελέγχου: τρία μπλοκ == … == μετά τρία prêt.
== Téléchargement des images (long la première fois : ~6 Go, ~12 Go avec OpenSearch) ==
== Démarrage ==
== Attente que chaque service soit prêt ==
elasticsearch prêt (0 s)
kibana prêt (0 s)
neo4j ..... prêt (15 s)
Le labo est prêt.
Kibana http://localhost:5601 (Dev Tools : menu ☰ → Management → Dev Tools)
Elasticsearch http://localhost:9200
Neo4j Browser http://localhost:7474 (utilisateur neo4j · mot de passe aiopsatlas2026)
Étape suivante : .\labo.ps1 importer puis .\labo.ps1 charger-grapheΤο πρώτο μπλοκ είναι κενό αν οι εικόνες υπάρχουν ήδη· το δεύτερο περιέχει τις γραμμές του Compose (Container labo-elasticsearch Started, ή Running αν ήδη έτρεχε)· το τρίτο προσθέτει μια τελεία κάθε τρία δευτερόλεπτα μέχρι το prêt (… s). Κρύο εργαστήριο: ένα με δύο λεπτά· εργαστήριο ήδη σε λειτουργία: prêt (0 s).
Αν βλέπεις κάτι άλλο: unhealthy, exited ή délai dépassé → .\labo.ps1 journal <service> και ο κατάλογος του μαθήματος 04· πιο συχνή αιτία σε Windows: η μνήμη που έχει διατεθεί στο Docker Desktop (Exited (137)).
etatΘα πληκτρολογήσεις το etat πολλές φορές· μάθε πρώτα να εξάγεις τους τέσσερις αριθμούς που μετράνε.
.\labo.ps1 etatΣημείο ελέγχου: πραγματική έξοδος από το μηχάνημα του μαθήματος, όπου το προφίλ OpenSearch της ενότητας 5 είναι ενεργό. Στο δικό σου, οι δύο γραμμές labo-opensearch… και ✔ OpenSearch αντικαθίστανται από — OpenSearch non démarré (profil optionnel : .\labo.ps1 demarrer opensearch).
== Conteneurs ==
NAME STATUS PORTS
labo-elasticsearch Up 10 hours (healthy) 0.0.0.0:9200->9200/tcp, [::]:9200->9200/tcp
labo-kibana Up 10 hours (healthy) 0.0.0.0:5601->5601/tcp, [::]:5601->5601/tcp
labo-neo4j Up 37 seconds (healthy) 0.0.0.0:7474->7474/tcp, [::]:7474->7474/tcp, 0.0.0.0:7687->7687/tcp, [::]:7687->7687/tcp
labo-opensearch Up 10 hours (healthy) 0.0.0.0:9201->9200/tcp, [::]:9201->9200/tcp
labo-opensearch-dashboards Up 10 hours 0.0.0.0:5602->5601/tcp, [::]:5602->5601/tcp
== Services ==
✔ Elasticsearch : {"status":"green","number_of_nodes":1}
index : acces 12000 avis 609 cours 504
✔ Kibana répond (http://localhost:5601)
✔ Neo4j répond — nœuds : 872
✔ OpenSearch : {"status":"green","number_of_nodes":1}Σημείωσε τους τέσσερις αναμενόμενους αριθμούς: 12000, 609, 504, 872. Σε ένα ολοκαίνουργιο εργαστήριο διαβάζεις index : aucun index du labo και nœuds : 0: φυσιολογικό, το βήμα A.4 τα συμπληρώνει.
Αν βλέπεις κάτι άλλο: ✘ Kibana ne répond pas encore μέσα στο λεπτό που ακολουθεί το demarrer → το Kibana τελειώνει να δημιουργεί τα εσωτερικά του index· ξαναπληκτρολόγησε etat τριάντα δευτερόλεπτα αργότερα.
Φόρτωσε τα index μετά τον γράφο, και ξανάτρεξε τις δύο εντολές μια δεύτερη φορά: οι μετρητές δεν πρέπει να αλλάξουν ούτε μία μονάδα.
.\labo.ps1 importer
.\labo.ps1 charger-graphe
.\labo.ps1 importer
.\labo.ps1 charger-grapheΣημείο ελέγχου: στο δεύτερο πέρασμα, το importer δηλώνει ότι τα index υπάρχουν ήδη και επιστρέφει τους ίδιους μετρητές:
== Import dans elasticsearch ==
— index cours existe déjà — conservé
✔ données cours chargées
— index avis existe déjà — conservé
✔ données avis chargées
— index acces existe déjà — conservé
✔ données acces chargées
index docs.count store.size
acces 12000 1.5mb
avis 609 74.8kb
cours 504 183.6kb
Import terminé. Attendu : cours = 504, avis = 609, acces = 12000.και το charger-graphe δίνει τον ίδιο απολογισμό με την πρώτη φορά:
== Chargement du graphe Neo4j ==
✔ contraintes et index en place
etiquette, noeuds
"Competence", 22
"Cours", 504
"Etudiant", 300
"Professeur", 30
"Ville", 16
Graphe chargé. Attendu : Competence 22, Cours 504, Etudiant 300, Professeur 30, Ville 16.Τα έγγραφα φέρουν το δικό τους _id (C0001, A00001…), άρα ένα δεύτερο _bulk αντικαθιστά κάθε έγγραφο αντί να το προσθέτει· στο Neo4j, όλα γίνονται με MERGE. Μόνο το store.size μπορεί να κινηθεί κατά μερικά kilobyte (η Lucene κρατά για λίγο τις παλιές εκδόσεις)· τα docs.count, ποτέ.
Αν βλέπεις κάτι άλλο: ένα docs.count διαφορετικό από 504 / 609 / 12000 → κάποιος έγραψε σε αυτά τα index· .\labo.ps1 reinitialiser μετά demarrer, importer, charger-graphe σου επαναφέρει την κατάσταση αναφοράς.
Το σενάριο λέει ότι όλα είναι καλά· επαλήθευσέ το χωρίς αυτό. Άνοιξε http://localhost:5601, μενού ☰ → Management → Outils de développement, και στείλε αυτά τα ερωτήματα ένα προς ένα (Ctrl + Enter ή το κουμπί ▶ «Cliquer pour envoyer la requête»).
GET _cluster/healthΣημείο ελέγχου: "status": "green", "unassigned_shards": 0, και 200 - OK κάτω δεξιά του πλαισίου απάντησης.
{
"cluster_name": "labo",
"status": "green",
"timed_out": false,
"number_of_nodes": 1,
"number_of_data_nodes": 1,
"active_primary_shards": 53,
"active_shards": 53,
…
"unassigned_shards": 0,
…
"active_shards_percent_as_number": 100
}GET _cat/indices/cours,avis,acces?v&s=indexΣημείο ελέγχου: τρεις γραμμές green, pri 1, rep 0, και οι τρεις μετρητές στο docs.count.
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size dataset.size
green open acces aii68fsfQXKyt5wqkE1mPA 1 0 12000 0 1.5mb 1.5mb 1.5mb
green open avis W9j_JrwJT4mdzpWcS5k7xg 1 0 609 0 74.8kb 74.8kb 74.8kb
green open cours pmq403ZgSZWeHJY9uNw1Qw 1 0 504 0 183.6kb 183.6kb 183.6kbΤελείωσε με GET cours/_count, GET avis/_count, GET acces/_count: "count": 504, 609, 12000. Έχεις τους ίδιους αριθμούς από δύο ανεξάρτητους δρόμους: το σενάριο (curl μέσα στο κοντέινερ) και το Dev Tools (μέσω του Kibana). Αν κάποια μέρα διαφέρουν, φταίει ο δρόμος, όχι τα δεδομένα.
Αν βλέπεις κάτι άλλο: "status": "yellow" → ένα index έχει μη κατανεμημένα replica, αδύνατο με τα mappings του kit (number_of_replicas: 0)· GET _cat/indices?v&health=yellow δείχνει τον ένοχο, συνήθως ένα index δημιουργημένο με το χέρι.
Ίδια άσκηση για το Neo4j. Άνοιξε http://localhost:7474, συνδέσου (neo4j / aiopsatlas2026, URL localhost:7687), πληκτρολόγησε στον επεξεργαστή neo4j$ και κάνε κλικ Run:
MATCH (n) RETURN labels(n)[0] AS label, count(*) ORDER BY labelΣημείο ελέγχου: ένα πλαίσιο με δύο προβολές, Table και Raw (όχι Graph: το ερώτημα επιστρέφει αριθμούς, όχι κόμβους), δύο στήλες label και count(*), πέντε ταξινομημένες γραμμές, και κάτω δεξιά Started streaming 5 records after … ms and completed after … ms.
label count(*)
"Competence" 22
"Cours" 504
"Etudiant" 300
"Professeur" 30
"Ville" 1622 + 504 + 300 + 30 + 16 = 872, ο αριθμός του etat. Το πλαίσιο Database information (εικονίδιο Database overview, το πρώτο της πλαϊνής μπάρας) εμφανίζει Nodes (872) και Relationships (3,712).
Αν βλέπεις κάτι άλλο: μια έκτη άγνωστη ετικέτα → κόμβοι δημιουργημένοι εκτός kit (η ενότητα 6 θα σου μάθει να τους διαγράφεις σωστά)· Nodes (0) → παρέλειψες το charger-graphe, επιστροφή στο βήμα A.4.
Ξέρεις πώς μοιάζει ένα υγιές εργαστήριο· πρόκαλεσε μια βλάβη της οποίας γνωρίζεις την αιτία για να μάθεις να τη διαβάζεις. Σταμάτησε μόνο το Kibana, με το Compose (όχι το arreter, που θα σταματούσε τα πάντα):
docker compose stop kibana Container labo-kibana Stopping
Container labo-kibana StoppedΜετά οι τρεις κινήσεις του μαθήματος 04, με τη σειρά: etat, το πρόγραμμα περιήγησης, journal.
Σημείο ελέγχου 1, .\labo.ps1 etat : η γραμμή labo-kibana εξαφανίστηκε από το μπλοκ == Conteneurs == (το Compose εμφανίζει εξ ορισμού μόνο τα κοντέινερ που τρέχουν) και το μπλοκ == Services == σημειώνει έναν σταυρό:
== Conteneurs ==
NAME STATUS PORTS
labo-elasticsearch Up 10 hours (healthy) 0.0.0.0:9200->9200/tcp, [::]:9200->9200/tcp
labo-neo4j Up 3 minutes (healthy) 0.0.0.0:7474->7474/tcp, [::]:7474->7474/tcp, 0.0.0.0:7687->7687/tcp, [::]:7687->7687/tcp
labo-opensearch Up 10 hours (healthy) 0.0.0.0:9201->9200/tcp, [::]:9201->9200/tcp
labo-opensearch-dashboards Up 10 hours 0.0.0.0:5602->5601/tcp, [::]:5602->5601/tcp
== Services ==
✔ Elasticsearch : {"status":"green","number_of_nodes":1}
index : acces 12000 avis 609 cours 504
✘ Kibana ne répond pas encore
✔ Neo4j répond — nœuds : 872
✔ OpenSearch : {"status":"green","number_of_nodes":1}Για να δεις παρόλα αυτά το σταματημένο κοντέινερ: docker compose ps -a εμφανίζει labo-kibana Exited (0) 31 seconds ago. Το 0 λέει «καθαρή διακοπή, ζητηθείσα»· ένα 137 θα έλεγε «σκοτώθηκε, μνήμη».
Σημείο ελέγχου 2, το πρόγραμμα περιήγησης: ανανέωσε το http://localhost:5601. Όχι «Kibana server is not ready yet» (αυτή η φράση προέρχεται από το Kibana, όμως δεν υπάρχει πια Kibana για να την πει) αλλά το σφάλμα σύνδεσης του ίδιου του προγράμματος περιήγησης: στο Chrome ή στο Edge, «Ce site est inaccessible», κωδικός ERR_CONNECTION_REFUSED. Κανείς δεν ακούει στη θύρα 5601. Να θυμάσαι: σελίδα Kibana που ζητά συγγνώμη = το Kibana τρέχει αλλά περιμένει το Elasticsearch· σφάλμα του προγράμματος περιήγησης = το Kibana δεν τρέχει.
Σημείο ελέγχου 3, .\labo.ps1 journal kibana : οι εκατό τελευταίες γραμμές τελειώνουν με μια καθαρή διακοπή, χρονοσημασμένη τη στιγμή που πληκτρολόγησες το stop:
labo-kibana | [2026-09-09T23:44:06.893+00:00][INFO ][root] SIGTERM received - initiating shutdown
labo-kibana | [2026-09-09T23:44:06.894+00:00][INFO ][root] Kibana is shutting down
labo-kibana | [2026-09-09T23:44:06.902+00:00][INFO ][plugins-system.standard] Stopping all plugins.
…
labo-kibana | [2026-09-09T23:44:07.265+00:00][INFO ][plugins-system.standard] All plugins stopped.Το SIGTERM received υπογράφει μια ζητηθείσα διακοπή (από σένα, από το docker compose stop, από επανεκκίνηση του Docker Desktop). Καμία γραμμή ERROR ούτε FATAL: τίποτα να επιδιορθωθεί, απλά χρειάζεται επανεκκίνηση. Οι δεκάδες γραμμές at OperatorSubscriber… ανάμεσα στις δύο είναι μια στοίβα κλήσεων που εκπέμπεται από ένα plugin κατά τη διακοπή: θόρυβος.
Αν βλέπεις κάτι άλλο: no configuration file provided: not found → δεν είσαι στον φάκελο του kit· no such service: kibana → πληκτρολόγησες το όνομα του κοντέινερ (labo-kibana) αντί για το όνομα υπηρεσίας του Compose (kibana).
Ξανάτρεξε την υπηρεσία και περίμενε το healthcheck της να ξαναγίνει πράσινο: 40 έως 60 δευτερόλεπτα, ο χρόνος να επανασυνδεθεί στο Elasticsearch και να επαληθεύσει τα εσωτερικά της index.
docker compose start kibana Container labo-elasticsearch Waiting
Container labo-elasticsearch Healthy
Container labo-kibana Starting
Container labo-kibana StartedΤο Compose επαλήθευσε πρώτα ότι το Elasticsearch ήταν Healthy (το depends_on … service_healthy του μαθήματος 03), μετά ξεκίνησε το Kibana. Παρακολούθησε κάθε πέντε δευτερόλεπτα:
docker inspect --format '{{.State.Health.Status}}' labo-kibanaΣημείο ελέγχου: starting για 40 έως 50 δευτερόλεπτα, μετά healthy (στο μηχάνημα του μαθήματος: starting από 0 έως 45 δ., healthy στα 50 δ.). Το etat δείχνει τότε ξανά τη γραμμή:
labo-kibana Up 52 seconds (healthy) 0.0.0.0:5601->5601/tcp, [::]:5601->5601/tcp
…
✔ Kibana répond (http://localhost:5601)και το journal kibana τελειώνει με τις γραμμές που θέλουμε να δούμε:
labo-kibana | [2026-09-09T23:47:49.408+00:00][INFO ][http.server.Kibana] http server running at http://0.0.0.0:5601
labo-kibana | [2026-09-09T23:47:50.374+00:00][INFO ][status] Kibana is now availableΞανάνοιξε το Dev Tools (αν πέσεις πάνω σε «Kibana server is not ready yet», ήσουν πιο γρήγορος από το healthy: περίμενε δέκα δευτερόλεπτα) και στείλε το ερώτημα που χρησιμοποιεί το ίδιο το etat:
GET _cluster/health?filter_path=status,number_of_nodes{
"status": "green",
"number_of_nodes": 1
}Το Elasticsearch απάντησε green καθ' όλη τη διάρκεια της βλάβης: το Kibana είναι ένα παράθυρο στα δεδομένα, όχι τα δεδομένα. Η διακοπή του Kibana δεν έσβησε ούτε επανευρετηρίασε τίποτα.
Αν βλέπεις κάτι άλλο: unhealthy μετά από δύο λεπτά → journal kibana και ψάξε για ECONNREFUSED (το Elasticsearch έπεσε στο μεταξύ)· port is already allocated → κάποιο άλλο πρόγραμμα πήρε τη θύρα 5601 κατά τη διάρκεια της διακοπής (μάθημα 04, βλάβη 1).
Αναγνωρίζεις μια σταματημένη υπηρεσία· μάθε τώρα να αναγνωρίζεις ένα λάθος ερώτημα σε μια υγιή υπηρεσία, τη συχνότερη σύγχυση στην αίθουσα. Στο Dev Tools, γράψε ένα ερώτημα αναζήτησης που επιστρέφει 404 με "type": "index_not_found_exception", μετά εξήγησε σε μία φράση γιατί το cluster παραμένει green.
Υπόδειξη: το Elasticsearch δεν μαντεύει ποτέ το όνομα ενός index. Διάλεξε ένα που δεν υπάρχει, με το πρόθεμα pratique-· τίποτα να δημιουργήσεις, τίποτα να γράψεις.
GET pratique-inexistant/_searchΑπάντηση, με το σήμα 404 - Not Found κάτω δεξιά του πλαισίου εξόδου:
{
"error": {
"root_cause": [
{
"type": "index_not_found_exception",
"reason": "no such index [pratique-inexistant]",
"resource.type": "index_or_alias",
"resource.id": "pratique-inexistant",
"index_uuid": "_na_",
"index": "pratique-inexistant"
}
],
"type": "index_not_found_exception",
"reason": "no such index [pratique-inexistant]",
…
},
"status": 404
}Αναμενόμενη εξήγηση: το 404 είναι μια φυσιολογική και πλήρης απάντηση του Elasticsearch: «κατάλαβα το ερώτημά σου, αλλά αυτός ο πόρος δεν υπάρχει». Η υπηρεσία τρέχει, το cluster παραμένει green· μόνο το όνομα χρειάζεται διόρθωση (GET _cat/indices?v δίνει τη λίστα). Στο βήμα A.7, δεν υπήρχε καμία απάντηση καθόλου.
Δύο παραλλαγές προς δοκιμή: το GET pratique-inexistant/_count?ignore_unavailable=true επιστρέφει 200 και "count": 0 (ζητάς να αγνοηθεί το απόν index)· το GET cours/_serch επιστρέφει 400 με "no handler found for uri [/cours/_serch] and method [GET]". Ένα 400: «δεν καταλαβαίνω το ερώτημα»· ένα 404: «καταλαβαίνω, αλλά αυτό δεν υπάρχει».
Μία μόνο εντολή αποδεικνύει ότι όλα έγιναν: etat, με το Kibana επιστρεμμένο και τους τέσσερις αριθμούς.
.\labo.ps1 etat== Conteneurs ==
NAME STATUS PORTS
labo-elasticsearch Up 10 hours (healthy) 0.0.0.0:9200->9200/tcp, [::]:9200->9200/tcp
labo-kibana Up About a minute (healthy) 0.0.0.0:5601->5601/tcp, [::]:5601->5601/tcp
labo-neo4j Up 7 minutes (healthy) 0.0.0.0:7474->7474/tcp, [::]:7474->7474/tcp, 0.0.0.0:7687->7687/tcp, [::]:7687->7687/tcp
labo-opensearch Up 10 hours (healthy) 0.0.0.0:9201->9200/tcp, [::]:9201->9200/tcp
labo-opensearch-dashboards Up 10 hours 0.0.0.0:5602->5601/tcp, [::]:5602->5601/tcp
== Services ==
✔ Elasticsearch : {"status":"green","number_of_nodes":1}
index : acces 12000 avis 609 cours 504
✔ Kibana répond (http://localhost:5601)
✔ Neo4j répond — nœuds : 872
✔ OpenSearch : {"status":"green","number_of_nodes":1}(Χωρίς το προφίλ OpenSearch, τα δύο κοντέινερ labo-opensearch… δεν εμφανίζονται και η τελευταία γραμμή λέει — OpenSearch non démarré …: είναι η αναμενόμενη κατάσταση μέχρι την ενότητα 5.)
prerequis τελειώνει με Tout est prêt.labo-elasticsearch, labo-kibana και labo-neo4j είναι Up … (healthy), το Kibana συμπεριλαμβανομένου.etat εμφανίζει "status":"green", acces 12000 avis 609 cours 504, nœuds : 872, αμετάβλητα μετά τη δεύτερη importer / charger-graphe.etat, το πρόγραμμα περιήγησης και το journal kibana όταν το Kibana είναι σταματημένο, και σε τι διαφέρει από ένα 404.etat παραπάνω (αντίγραφο ή στιγμιότυπο) ως παραδοτέο.Αυτή η πρακτική δεν δημιουργεί τίποτα: ούτε index, ούτε κόμβο, ούτε αντικείμενο Kibana. Δύο πράγματα να διασφαλίσεις: ότι το Kibana τρέχει (αλλιώς docker compose start kibana από τον φάκελο του kit), και ότι κανένα index εργασίας δεν έχει μείνει:
GET _cat/indices/pratique-*?vΑναμενόμενη απάντηση: η γραμμή κεφαλίδας μόνη (health status index uuid pri rep docs.count …). Μην αγγίξεις τα cours, avis, acces ούτε τον γράφο: χρησιμεύουν σε όλες τις επόμενες ενότητες.
Όλες οι εντολές αυτού του παραρτήματος πληκτρολογούνται σε τερματικό bash (ή zsh), με ./labo.sh …. Οι έξοδοι είναι πανομοιότυπες με αυτές των Windows, εκτός από το όνομα του σεναρίου: το kit είναι το ίδιο, μόνο οι εκκινητές αλλάζουν.
docker σε Linux: το docker info πρέπει να απαντά), το kit κλωνοποιημένο, ένα τερματικό ανοιχτό μέσα στον φάκελο του kit (αυτόν που περιέχει το docker-compose.yml και το labo.sh).Permission denied στο ./labo.sh: chmod +x labo.sh, μία φορά../labo.sh etat πρέπει να εμφανίζει (healthy) παντού, "status":"green", acces 12000 avis 609 cours 504 και nœuds : 872.elasticsearch/requetes/01-pratique-demarrer-verifier-reparer.txt. Κανένα δεν γράφει στο cluster.Πριν ανάψεις οτιδήποτε, κάνε το σενάριο να μιλήσει.
./labo.sh prerequisΣημείο ελέγχου: μόνο πράσινα τικ και η τελική φράση.
== Prérequis ==
✔ docker : Docker version 29.3.1, build c2be9cc
✔ le démon Docker répond
✔ docker compose : 5.1.1
✔ mémoire disponible pour Docker : 31 Go
✔ processeurs : 20
✔ port 9200 : utilisé par le labo lui-même
✔ port 5601 : utilisé par le labo lui-même
✔ port 7474 : utilisé par le labo lui-même
✔ port 7687 : utilisé par le labo lui-même
Tout est prêt. Lancez : ./labo.sh demarrerΣτο δικό σου μηχάνημα, εκδόσεις και μνήμη διαφέρουν, και αν το εργαστήριο δεν έχει τρέξει ποτέ οι τέσσερις τελευταίες γραμμές λένε port 9200 : libre. Και οι δύο αναγνώσεις είναι καλές.
Αν βλέπεις κάτι άλλο: ένας σταυρός ✘ περιέχει τη λύση του μέσα στη φράση (Docker όχι εκκινημένο, μνήμη κάτω από 4 GB, θύρα κατειλημμένη)· διόρθωσε, ξανατρέξε, προχώρα μόνο με την πράσινη γραμμή. Σε Linux native, η μνήμη είναι αυτή του μηχανήματος· σε macOS και WSL 2, είναι αυτή που διατέθηκε στο Docker Desktop → Settings → Resources.
Ξεκίνα την εκκίνηση και, αυτή τη φορά, διάβασε ό,τι κυλά αντί να περιμένεις το τέλος.
./labo.sh demarrerΣημείο ελέγχου: τρία μπλοκ == … == μετά τρία prêt.
== Téléchargement des images (long la première fois : ~6 Go, ~12 Go avec OpenSearch) ==
== Démarrage ==
== Attente que chaque service soit prêt ==
elasticsearch prêt (0 s)
kibana prêt (0 s)
neo4j ..... prêt (15 s)
Le labo est prêt.
Kibana http://localhost:5601 (Dev Tools : menu ☰ → Management → Dev Tools)
Elasticsearch http://localhost:9200
Neo4j Browser http://localhost:7474 (utilisateur neo4j · mot de passe aiopsatlas2026)
Étape suivante : ./labo.sh importer puis ./labo.sh charger-grapheΤο πρώτο μπλοκ είναι κενό αν οι εικόνες υπάρχουν ήδη· το δεύτερο περιέχει τις γραμμές του Compose (Container labo-elasticsearch Started, ή Running αν ήδη έτρεχε)· το τρίτο προσθέτει μια τελεία κάθε τρία δευτερόλεπτα μέχρι το prêt (… s). Κρύο εργαστήριο: ένα με δύο λεπτά· εργαστήριο ήδη σε λειτουργία: prêt (0 s).
Αν βλέπεις κάτι άλλο: unhealthy, exited ή délai dépassé → ./labo.sh journal <service> και ο κατάλογος του μαθήματος 04· πιο συχνές αιτίες: η μνήμη (Exited (137)) και, σε Linux native, το vm.max_map_count πολύ χαμηλό (sudo sysctl -w vm.max_map_count=262144, μετά ./labo.sh demarrer).
etatΘα πληκτρολογήσεις το etat πολλές φορές· μάθε πρώτα να εξάγεις τους τέσσερις αριθμούς που μετράνε.
./labo.sh etatΣημείο ελέγχου: πραγματική έξοδος από το μηχάνημα του μαθήματος, όπου το προφίλ OpenSearch της ενότητας 5 είναι ενεργό. Στο δικό σου, οι δύο γραμμές labo-opensearch… και ✔ OpenSearch αντικαθίστανται από — OpenSearch non démarré (profil optionnel : ./labo.sh demarrer opensearch).
== Conteneurs ==
NAME STATUS PORTS
labo-elasticsearch Up 10 hours (healthy) 0.0.0.0:9200->9200/tcp, [::]:9200->9200/tcp
labo-kibana Up 10 hours (healthy) 0.0.0.0:5601->5601/tcp, [::]:5601->5601/tcp
labo-neo4j Up 37 seconds (healthy) 0.0.0.0:7474->7474/tcp, [::]:7474->7474/tcp, 0.0.0.0:7687->7687/tcp, [::]:7687->7687/tcp
labo-opensearch Up 10 hours (healthy) 0.0.0.0:9201->9200/tcp, [::]:9201->9200/tcp
labo-opensearch-dashboards Up 10 hours 0.0.0.0:5602->5601/tcp, [::]:5602->5601/tcp
== Services ==
✔ Elasticsearch : {"status":"green","number_of_nodes":1}
index : acces 12000 avis 609 cours 504
✔ Kibana répond (http://localhost:5601)
✔ Neo4j répond — nœuds : 872
✔ OpenSearch : {"status":"green","number_of_nodes":1}Σημείωσε τους τέσσερις αναμενόμενους αριθμούς: 12000, 609, 504, 872. Σε ένα ολοκαίνουργιο εργαστήριο διαβάζεις index : aucun index du labo και nœuds : 0: φυσιολογικό, το βήμα B.4 τα συμπληρώνει.
Αν βλέπεις κάτι άλλο: ✘ Kibana ne répond pas encore μέσα στο λεπτό που ακολουθεί το demarrer → το Kibana τελειώνει να δημιουργεί τα εσωτερικά του index· ξαναπληκτρολόγησε etat τριάντα δευτερόλεπτα αργότερα.
Φόρτωσε τα index μετά τον γράφο, και ξανάτρεξε τις δύο εντολές μια δεύτερη φορά: οι μετρητές δεν πρέπει να αλλάξουν ούτε μία μονάδα.
./labo.sh importer
./labo.sh charger-graphe
./labo.sh importer
./labo.sh charger-grapheΣημείο ελέγχου: στο δεύτερο πέρασμα, το importer δηλώνει ότι τα index υπάρχουν ήδη και επιστρέφει τους ίδιους μετρητές:
== Import dans elasticsearch ==
— index cours existe déjà — conservé
✔ données cours chargées
— index avis existe déjà — conservé
✔ données avis chargées
— index acces existe déjà — conservé
✔ données acces chargées
index docs.count store.size
acces 12000 1.5mb
avis 609 74.8kb
cours 504 183.6kb
Import terminé. Attendu : cours = 504, avis = 609, acces = 12000.και το charger-graphe δίνει τον ίδιο απολογισμό με την πρώτη φορά:
== Chargement du graphe Neo4j ==
✔ contraintes et index en place
etiquette, noeuds
"Competence", 22
"Cours", 504
"Etudiant", 300
"Professeur", 30
"Ville", 16
Graphe chargé. Attendu : Competence 22, Cours 504, Etudiant 300, Professeur 30, Ville 16.Τα έγγραφα φέρουν το δικό τους _id (C0001, A00001…), άρα ένα δεύτερο _bulk αντικαθιστά κάθε έγγραφο αντί να το προσθέτει· στο Neo4j, όλα γίνονται με MERGE. Μόνο το store.size μπορεί να κινηθεί κατά μερικά kilobyte (η Lucene κρατά για λίγο τις παλιές εκδόσεις)· τα docs.count, ποτέ.
Αν βλέπεις κάτι άλλο: ένα docs.count διαφορετικό από 504 / 609 / 12000 → κάποιος έγραψε σε αυτά τα index· ./labo.sh reinitialiser μετά demarrer, importer, charger-graphe σου επαναφέρει την κατάσταση αναφοράς.
Το σενάριο λέει ότι όλα είναι καλά· επαλήθευσέ το χωρίς αυτό. Άνοιξε http://localhost:5601, μενού ☰ → Management → Outils de développement, και στείλε αυτά τα ερωτήματα ένα προς ένα (Ctrl + Enter, ή Cmd + Enter σε macOS, ή το κουμπί ▶ «Cliquer pour envoyer la requête»).
GET _cluster/healthΣημείο ελέγχου: "status": "green", "unassigned_shards": 0, και 200 - OK κάτω δεξιά του πλαισίου απάντησης.
{
"cluster_name": "labo",
"status": "green",
"timed_out": false,
"number_of_nodes": 1,
"number_of_data_nodes": 1,
"active_primary_shards": 53,
"active_shards": 53,
…
"unassigned_shards": 0,
…
"active_shards_percent_as_number": 100
}GET _cat/indices/cours,avis,acces?v&s=indexΣημείο ελέγχου: τρεις γραμμές green, pri 1, rep 0, και οι τρεις μετρητές στο docs.count.
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size dataset.size
green open acces aii68fsfQXKyt5wqkE1mPA 1 0 12000 0 1.5mb 1.5mb 1.5mb
green open avis W9j_JrwJT4mdzpWcS5k7xg 1 0 609 0 74.8kb 74.8kb 74.8kb
green open cours pmq403ZgSZWeHJY9uNw1Qw 1 0 504 0 183.6kb 183.6kb 183.6kbΤελείωσε με GET cours/_count, GET avis/_count, GET acces/_count: "count": 504, 609, 12000. Έχεις τους ίδιους αριθμούς από δύο ανεξάρτητους δρόμους: το σενάριο (curl μέσα στο κοντέινερ) και το Dev Tools (μέσω του Kibana). Αν κάποια μέρα διαφέρουν, φταίει ο δρόμος, όχι τα δεδομένα.
Σε bash, έχεις ακόμα και τρίτο δρόμο, χωρίς πρόγραμμα περιήγησης:
curl -s 'http://localhost:9200/_cat/indices/cours,avis,acces?v&s=index'Αν βλέπεις κάτι άλλο: "status": "yellow" → ένα index έχει μη κατανεμημένα replica, αδύνατο με τα mappings του kit (number_of_replicas: 0)· GET _cat/indices?v&health=yellow δείχνει τον ένοχο, συνήθως ένα index δημιουργημένο με το χέρι.
Ίδια άσκηση για το Neo4j. Άνοιξε http://localhost:7474, συνδέσου (neo4j / aiopsatlas2026, URL localhost:7687), πληκτρολόγησε στον επεξεργαστή neo4j$ και κάνε κλικ Run:
MATCH (n) RETURN labels(n)[0] AS label, count(*) ORDER BY labelΣημείο ελέγχου: ένα πλαίσιο με δύο προβολές, Table και Raw (όχι Graph: το ερώτημα επιστρέφει αριθμούς, όχι κόμβους), δύο στήλες label και count(*), πέντε ταξινομημένες γραμμές, και κάτω δεξιά Started streaming 5 records after … ms and completed after … ms.
label count(*)
"Competence" 22
"Cours" 504
"Etudiant" 300
"Professeur" 30
"Ville" 1622 + 504 + 300 + 30 + 16 = 872, ο αριθμός του etat. Το πλαίσιο Database information (εικονίδιο Database overview, το πρώτο της πλαϊνής μπάρας) εμφανίζει Nodes (872) και Relationships (3,712).
Αν βλέπεις κάτι άλλο: μια έκτη άγνωστη ετικέτα → κόμβοι δημιουργημένοι εκτός kit (η ενότητα 6 θα σου μάθει να τους διαγράφεις σωστά)· Nodes (0) → παρέλειψες το charger-graphe, επιστροφή στο βήμα B.4.
Ξέρεις πώς μοιάζει ένα υγιές εργαστήριο· πρόκαλεσε μια βλάβη της οποίας γνωρίζεις την αιτία για να μάθεις να τη διαβάζεις. Σταμάτησε μόνο το Kibana, με το Compose (όχι το arreter, που θα σταματούσε τα πάντα):
docker compose stop kibana Container labo-kibana Stopping
Container labo-kibana StoppedΜετά οι τρεις κινήσεις του μαθήματος 04, με τη σειρά: etat, το πρόγραμμα περιήγησης, journal.
Σημείο ελέγχου 1, ./labo.sh etat : η γραμμή labo-kibana εξαφανίστηκε από το μπλοκ == Conteneurs == (το Compose εμφανίζει εξ ορισμού μόνο τα κοντέινερ που τρέχουν) και το μπλοκ == Services == σημειώνει έναν σταυρό:
== Conteneurs ==
NAME STATUS PORTS
labo-elasticsearch Up 10 hours (healthy) 0.0.0.0:9200->9200/tcp, [::]:9200->9200/tcp
labo-neo4j Up 3 minutes (healthy) 0.0.0.0:7474->7474/tcp, [::]:7474->7474/tcp, 0.0.0.0:7687->7687/tcp, [::]:7687->7687/tcp
labo-opensearch Up 10 hours (healthy) 0.0.0.0:9201->9200/tcp, [::]:9201->9200/tcp
labo-opensearch-dashboards Up 10 hours 0.0.0.0:5602->5601/tcp, [::]:5602->5601/tcp
== Services ==
✔ Elasticsearch : {"status":"green","number_of_nodes":1}
index : acces 12000 avis 609 cours 504
✘ Kibana ne répond pas encore
✔ Neo4j répond — nœuds : 872
✔ OpenSearch : {"status":"green","number_of_nodes":1}Για να δεις παρόλα αυτά το σταματημένο κοντέινερ: docker compose ps -a εμφανίζει labo-kibana Exited (0) 31 seconds ago. Το 0 λέει «καθαρή διακοπή, ζητηθείσα»· ένα 137 θα έλεγε «σκοτώθηκε, μνήμη».
Σημείο ελέγχου 2, το πρόγραμμα περιήγησης: ανανέωσε το http://localhost:5601. Όχι «Kibana server is not ready yet» (αυτή η φράση προέρχεται από το Kibana, όμως δεν υπάρχει πια Kibana για να την πει) αλλά το σφάλμα σύνδεσης του ίδιου του προγράμματος περιήγησης: στο Chrome, «Ce site est inaccessible», κωδικός ERR_CONNECTION_REFUSED· στο Firefox, «Impossible de se connecter». Στη γραμμή εντολών, το curl -s http://localhost:5601 || echo REFUSE εμφανίζει REFUSE: κανείς δεν ακούει στη θύρα 5601. Να θυμάσαι: σελίδα Kibana που ζητά συγγνώμη = το Kibana τρέχει αλλά περιμένει το Elasticsearch· σφάλμα του προγράμματος περιήγησης = το Kibana δεν τρέχει.
Σημείο ελέγχου 3, ./labo.sh journal kibana : οι εκατό τελευταίες γραμμές τελειώνουν με μια καθαρή διακοπή, χρονοσημασμένη τη στιγμή που πληκτρολόγησες το stop:
labo-kibana | [2026-09-09T23:44:06.893+00:00][INFO ][root] SIGTERM received - initiating shutdown
labo-kibana | [2026-09-09T23:44:06.894+00:00][INFO ][root] Kibana is shutting down
labo-kibana | [2026-09-09T23:44:06.902+00:00][INFO ][plugins-system.standard] Stopping all plugins.
…
labo-kibana | [2026-09-09T23:44:07.265+00:00][INFO ][plugins-system.standard] All plugins stopped.Το SIGTERM received υπογράφει μια ζητηθείσα διακοπή (από σένα, από το docker compose stop, από επανεκκίνηση του Docker). Καμία γραμμή ERROR ούτε FATAL: τίποτα να επιδιορθωθεί, απλά χρειάζεται επανεκκίνηση. Οι δεκάδες γραμμές at OperatorSubscriber… ανάμεσα στις δύο είναι μια στοίβα κλήσεων που εκπέμπεται από ένα plugin κατά τη διακοπή: θόρυβος.
Αν βλέπεις κάτι άλλο: no configuration file provided: not found → δεν είσαι στον φάκελο του kit· no such service: kibana → πληκτρολόγησες το όνομα του κοντέινερ (labo-kibana) αντί για το όνομα υπηρεσίας του Compose (kibana).
Ξανάτρεξε την υπηρεσία και περίμενε το healthcheck της να ξαναγίνει πράσινο: 40 έως 60 δευτερόλεπτα, ο χρόνος να επανασυνδεθεί στο Elasticsearch και να επαληθεύσει τα εσωτερικά της index.
docker compose start kibana Container labo-elasticsearch Waiting
Container labo-elasticsearch Healthy
Container labo-kibana Starting
Container labo-kibana StartedΤο Compose επαλήθευσε πρώτα ότι το Elasticsearch ήταν Healthy (το depends_on … service_healthy του μαθήματος 03), μετά ξεκίνησε το Kibana. Παρακολούθησε κάθε πέντε δευτερόλεπτα:
docker inspect --format '{{.State.Health.Status}}' labo-kibanaή, για να μην ξαναπληκτρολογείς, το watch -n 5 docker inspect --format '{{.State.Health.Status}}' labo-kibana (Ctrl + C για έξοδο· το watch λείπει από το macOS εξ ορισμού, ξαναπληκτρολόγησε την εντολή με το χέρι).
Σημείο ελέγχου: starting για 40 έως 50 δευτερόλεπτα, μετά healthy (στο μηχάνημα του μαθήματος: starting από 0 έως 45 δ., healthy στα 50 δ.). Το etat δείχνει τότε ξανά τη γραμμή:
labo-kibana Up 52 seconds (healthy) 0.0.0.0:5601->5601/tcp, [::]:5601->5601/tcp
…
✔ Kibana répond (http://localhost:5601)και το journal kibana τελειώνει με τις γραμμές που θέλουμε να δούμε:
labo-kibana | [2026-09-09T23:47:49.408+00:00][INFO ][http.server.Kibana] http server running at http://0.0.0.0:5601
labo-kibana | [2026-09-09T23:47:50.374+00:00][INFO ][status] Kibana is now availableΞανάνοιξε το Dev Tools (αν πέσεις πάνω σε «Kibana server is not ready yet», ήσουν πιο γρήγορος από το healthy: περίμενε δέκα δευτερόλεπτα) και στείλε το ερώτημα που χρησιμοποιεί το ίδιο το etat:
GET _cluster/health?filter_path=status,number_of_nodes{
"status": "green",
"number_of_nodes": 1
}Το Elasticsearch απάντησε green καθ' όλη τη διάρκεια της βλάβης: το Kibana είναι ένα παράθυρο στα δεδομένα, όχι τα δεδομένα. Η διακοπή του Kibana δεν έσβησε ούτε επανευρετηρίασε τίποτα.
Αν βλέπεις κάτι άλλο: unhealthy μετά από δύο λεπτά → journal kibana και ψάξε για ECONNREFUSED (το Elasticsearch έπεσε στο μεταξύ)· port is already allocated → κάποιο άλλο πρόγραμμα πήρε τη θύρα 5601 κατά τη διάρκεια της διακοπής (μάθημα 04, βλάβη 1).
Αναγνωρίζεις μια σταματημένη υπηρεσία· μάθε τώρα να αναγνωρίζεις ένα λάθος ερώτημα σε μια υγιή υπηρεσία, τη συχνότερη σύγχυση στην αίθουσα. Στο Dev Tools, γράψε ένα ερώτημα αναζήτησης που επιστρέφει 404 με "type": "index_not_found_exception", μετά εξήγησε σε μία φράση γιατί το cluster παραμένει green.
Υπόδειξη: το Elasticsearch δεν μαντεύει ποτέ το όνομα ενός index. Διάλεξε ένα που δεν υπάρχει, με το πρόθεμα pratique-· τίποτα να δημιουργήσεις, τίποτα να γράψεις.
GET pratique-inexistant/_searchΑπάντηση, με το σήμα 404 - Not Found κάτω δεξιά του πλαισίου εξόδου:
{
"error": {
"root_cause": [
{
"type": "index_not_found_exception",
"reason": "no such index [pratique-inexistant]",
"resource.type": "index_or_alias",
"resource.id": "pratique-inexistant",
"index_uuid": "_na_",
"index": "pratique-inexistant"
}
],
"type": "index_not_found_exception",
"reason": "no such index [pratique-inexistant]",
…
},
"status": 404
}Το ίδιο στη γραμμή εντολών, για να δεις τον γυμνό κωδικό HTTP: το curl -s -o /dev/null -w '%{http_code}\n' http://localhost:9200/pratique-inexistant/_search εμφανίζει 404.
Αναμενόμενη εξήγηση: το 404 είναι μια φυσιολογική και πλήρης απάντηση του Elasticsearch: «κατάλαβα το ερώτημά σου, αλλά αυτός ο πόρος δεν υπάρχει». Η υπηρεσία τρέχει, το cluster παραμένει green· μόνο το όνομα χρειάζεται διόρθωση (GET _cat/indices?v δίνει τη λίστα). Στο βήμα B.7, δεν υπήρχε καμία απάντηση καθόλου.
Δύο παραλλαγές προς δοκιμή: το GET pratique-inexistant/_count?ignore_unavailable=true επιστρέφει 200 και "count": 0 (ζητάς να αγνοηθεί το απόν index)· το GET cours/_serch επιστρέφει 400 με "no handler found for uri [/cours/_serch] and method [GET]". Ένα 400: «δεν καταλαβαίνω το ερώτημα»· ένα 404: «καταλαβαίνω, αλλά αυτό δεν υπάρχει».
Μία μόνο εντολή αποδεικνύει ότι όλα έγιναν: etat, με το Kibana επιστρεμμένο και τους τέσσερις αριθμούς.
./labo.sh etat== Conteneurs ==
NAME STATUS PORTS
labo-elasticsearch Up 10 hours (healthy) 0.0.0.0:9200->9200/tcp, [::]:9200->9200/tcp
labo-kibana Up About a minute (healthy) 0.0.0.0:5601->5601/tcp, [::]:5601->5601/tcp
labo-neo4j Up 7 minutes (healthy) 0.0.0.0:7474->7474/tcp, [::]:7474->7474/tcp, 0.0.0.0:7687->7687/tcp, [::]:7687->7687/tcp
labo-opensearch Up 10 hours (healthy) 0.0.0.0:9201->9200/tcp, [::]:9201->9200/tcp
labo-opensearch-dashboards Up 10 hours 0.0.0.0:5602->5601/tcp, [::]:5602->5601/tcp
== Services ==
✔ Elasticsearch : {"status":"green","number_of_nodes":1}
index : acces 12000 avis 609 cours 504
✔ Kibana répond (http://localhost:5601)
✔ Neo4j répond — nœuds : 872
✔ OpenSearch : {"status":"green","number_of_nodes":1}(Χωρίς το προφίλ OpenSearch, τα δύο κοντέινερ labo-opensearch… δεν εμφανίζονται και η τελευταία γραμμή λέει — OpenSearch non démarré …: είναι η αναμενόμενη κατάσταση μέχρι την ενότητα 5.)
prerequis τελειώνει με Tout est prêt.labo-elasticsearch, labo-kibana και labo-neo4j είναι Up … (healthy), το Kibana συμπεριλαμβανομένου.etat εμφανίζει "status":"green", acces 12000 avis 609 cours 504, nœuds : 872, αμετάβλητα μετά τη δεύτερη importer / charger-graphe.etat, το πρόγραμμα περιήγησης και το journal kibana όταν το Kibana είναι σταματημένο, και σε τι διαφέρει από ένα 404.etat παραπάνω (αντίγραφο ή στιγμιότυπο) ως παραδοτέο.Αυτή η πρακτική δεν δημιουργεί τίποτα: ούτε index, ούτε κόμβο, ούτε αντικείμενο Kibana. Δύο πράγματα να διασφαλίσεις: ότι το Kibana τρέχει (αλλιώς docker compose start kibana από τον φάκελο του kit), και ότι κανένα index εργασίας δεν έχει μείνει:
GET _cat/indices/pratique-*?vΑναμενόμενη απάντηση: η γραμμή κεφαλίδας μόνη (health status index uuid pri rep docs.count …). Μην αγγίξεις τα cours, avis, acces ούτε τον γράφο: χρησιμεύουν σε όλες τις επόμενες ενότητες.
Το docker compose stop kibana απαντά no configuration file provided: not found → Το Compose ψάχνει το docker-compose.yml στον τρέχοντα φάκελο. Κάνε cd προς τη ρίζα του kit (αυτή που περιέχει το labo.sh και το labo.ps1) και ξαναρχίσε. Το σενάριο επανατοποθετείται μόνο του· οι εντολές docker compose πληκτρολογημένες με το χέρι, όχι.
Μετά το stop, το etat δεν δείχνει πια το labo-kibana και νομίζεις ότι το διέγραψες → Όχι: το docker compose ps κρύβει τα σταματημένα κοντέινερ. Το docker compose ps -a το απαριθμεί ως Exited (0), και το docker compose start kibana το επανεκκινεί με τα δεδομένα του. Ένα πραγματικά διαγραμμένο κοντέινερ δεν θα εμφανιζόταν ούτε καν με το -a· το demarrer θα το ξαναδημιουργούσε.
Το Kibana παραμένει starting μετά περνά unhealthy, και το journal kibana επαναλαμβάνει Unable to retrieve version information from Elasticsearch nodes. connect ECONNREFUSED 172.x.x.x:9200 → Το Kibana επέστρεψε αλλά το Elasticsearch έπεσε στο μεταξύ (συχνά Exited (137), η μνήμη). Επιδιόρθωσε πρώτα το Elasticsearch (μάθημα 04, βλάβη 2)· το Kibana επανασυνδέεται μόνο του.
Το Dev Tools επιστρέφει 400 αντί για το αναμενόμενο 404, με no handler found for uri [/pratique-inexistant/_serch] and method [GET] → Το λάθος αφορά το API (_serch), όχι το index. Το Elasticsearch επικυρώνει πρώτα τη διαδρομή, μετά το index: διόρθωσε σε _search και το 404 εμφανίζεται.
Μόνο Windows — σε PowerShell 5.1, το demarrer εμφανίζει με κόκκινο docker : Image docker.elastic.co/kibana/kibana:9.5.3 Pulling … NativeCommandError αλλά τελειώνει με Le labo est prêt → Αυτό συμβαίνει μόνο αν ανακατευθύνεις την έξοδο (2>&1, | Tee-Object): το Compose γράφει την πρόοδό του στη ροή σφάλματος και το PowerShell 5.1 την ντύνει ως εξαίρεση. Δεν είναι σφάλμα· εκτέλεσε το σενάριο χωρίς ανακατεύθυνση, ή πέρνα σε PowerShell 7.
Μόνο Windows — το .\labo.ps1 απορρίπτεται: «l'exécution de scripts est désactivée sur ce système» → Set-ExecutionPolicy -Scope CurrentUser RemoteSigned, απάντησε O, ξανατρέξε. Μία φορά ανά μηχάνημα.
Μόνο Linux native — το Elasticsearch βγαίνει με Exited (78) και το journal elasticsearch λέει max virtual memory areas vm.max_map_count [65530] is too low → sudo sysctl -w vm.max_map_count=262144 μετά ./labo.sh demarrer. Για να επιβιώσει μετά από επανεκκίνηση: πρόσθεσε vm.max_map_count=262144 στο /etc/sysctl.conf.
macOS και bash — το ./labo.sh απαντά Permission denied → chmod +x labo.sh, μία φορά. Αν bash: ./labo.sh: /bin/bash^M: bad interpreter, το αρχείο έχει τέλη γραμμής Windows: git config core.autocrlf input μετά ξανακλωνοποίηση, ή sed -i '' 's/\r$//' labo.sh.