ورشة عمل أساسيّة 2 — Neo4j : عقدة واحدة وخصائصها وعلاقة واحدة

تدريب موجَّه13 دقيقة
المدة
25 دقيقة
الوحدة
1/7
المتطلّبات
المختبر يعمل (etat يعرض nœuds : 872)، Neo4j Browser مفتوح ومتّصل
ستبني
عقدتين لك ، أليس وبوب ، مرتبطتين بسهم ، ثم مرتبطتين بالرسم البيانيّ الحقيقيّ للدورة ، قبل حذف كلّ شيء بشكل نظيف
التسليمة
جدول الخطوة 8 (أليس ، علاقتاها ، أهدافهما)

كيف تقرأ هذه الصفحة. عشر خطوات ، استعلام واحد في كلّ مرّة. لكلّ خطوة : الاستعلام الذي تكتبه ، ما يعرضه Neo4j Browser ، وما يجب النظر إليه. اكتب كلّ استعلام بنفسك. فقرات « لفهم أعمق » اختياريّة. إذا لم يكن المختبر مُشغَّلًا أو إذا لم يقل etat nœuds : 872، ارجع إلى التطبيق الموجّه، القسم بإيجاز (الحزمة : https://github.com/hrhouma2/aiopsatlas-recherche-graphes-labo-fr).

الهدف

في التطبيق الموجّه ، عددت 872 عقدة و3712 علاقة حمَّلها نصّ برمجيّ ، ونظرت إلى فقّاعات وأسهم رسمها شخص آخر. هنا ، ترسم أنت خاصّتك. عقدة فارغة أوّلًا ، لترى أنّ العقدة هي هذا فقط ؛ ثم تُعطيها اسمًا وعمرًا ومدينة ؛ ثم عقدة ثانية ، وسهمًا بين الاثنتين ؛ ثم سهمًا نحو دورة من مجموعة البيانات الحقيقيّة. في النهاية ، يرتفع عدّاد etat إلى 874، وتُعيده إلى 872 بحذف ما أنشأته ، مع الخطأ الذي يجب رؤيته مرّة لفهم ما هي العلاقة.

المصطلحات في صورة واحدة

العقدة هي فقّاعة. العلامة تقول من أيّ نوع هذه الفقّاعة (Personne, Cours, Ville) : وهي معادلة اسم الجدول. الخاصيّة هي قيمة مكتوبة في الفقّاعة (nom: "Alice", age: 30) : عمود. العلاقة هي سهم بين فقّاعتين ؛ لها نوع بحروف كبيرة (CONNAIT, AIMEاتّجاه، ويمكن أن تحمل خصائصها الخاصّة. في SQL ، سيكون السهم مفتاحًا أجنبيًّا أو جدول ربط ؛ هنا إنّه كائن مستقلّ بذاته ، تُنشئه وتقرؤه وتحذفه كعقدة.

Neo4jقاعدة SQL كلاسيكيّةفي هذا التطبيق
عقدةصفّأليس ، بوب
علامةجدولPersonne
خاصيّةعمودnom, age, ville
علاقةمفتاح أجنبيّ / جدول ربطCONNAIT, AIME
نوع العلاقةاسم جدول الربطCONNAIT
خاصيّة العلاقةعمود جدول الربطdepuis: 2024

قاعدتان في كتابة Cypher يجب حفظهما : تُكتَب الفقّاعة بين أقواس (p:Personne)، سهم بين أقواس معقَّفة بشرطة ورأس سهم -[:CONNAIT]->. الحرف قبل النقطتين (p, a, r) اسم مؤقّت تختاره لإعادة استخدام الكائن في الاستعلام نفسه.

أين تكتب ، وكيف تقرأ الاستجابة

افتح http://localhost:7474، سجّل الدخول (neo4j / aiopsatlas2026، URL localhost:7687). المحرّر هو السطر neo4j$ في الأعلى. تُرسل بـ Ctrl + Enter (Cmd + Enter على macOS) أو الزرّ ▶ إلى اليمين. يفتح كلّ استعلام إطارًا تحت المحرّر ، الأحدث في الأعلى.

يعرض الإطار شيئين. في الأعلى ، سطر أخضر للحصيلة : Created 1 node, added 1 label، Set 1 property، Deleted 2 nodes… هو من يقول لك ما غيّره الاستعلام. أدناه ، إذا كان للاستعلام RETURN، النتيجة بثلاث معاينات : Graph (الفقّاعات والأسهم)، Table (القيم في أعمدة)، Raw (JSON خام). استعلام بلا RETURN لا يعرض إلّا الحصيلة و Completed after … ms.

الخطوة 1 — إنشاء عقدة ، فقط علامة

cypher
CREATE (p:Personne) RETURN p

ما يطلبه الاستعلام : أنشئ فقّاعة تحمل العلامة Personne، سمّها p طوال الاستعلام ، وأظهرها لي.

الحصيلة : Created 1 node, added 1 label، ثم Started streaming 1 record after 1 ms and completed after 2 ms.

انظر إلى : معاينة Graph ، فقّاعة واحدة فقط ، وإلى اليمين في Results overview : Nodes (1) مع الرقعة Personne (1). اضغط على الفقّاعة : لا يسرد اللوحة أيّ خاصيّة. هذه عقدة أدنى : علامة ، لا شيء آخر. معادل SQL : INSERT INTO personne DEFAULT VALUES، في جدول لا يمتلك أيّ عمود.

لفهم أعمق
  • الأكثر بساطة حتّى ، CREATE (n) بدون علامة ، يعمل أيضًا. يقبله Neo4j ، لكنّك لن تستطيع إيجادها بعد الآن إلّا بالمرور على كلّ فقّاعات الرسم البيانيّ : صفّ بلا جدول. لا تفعل ذلك ؛ علامة ، دائمًا.
  • Personne علامة جديدة في هذا الرسم البيانيّ ، الذي لم يكن يعرف إلّا خمسة (Competence, Cours, Etudiant, Professeur, Ville). لم تحتَج إلى تعريف أيّ شيء : الفقّاعة الأولى التي تحمل علامة تُنشئها. كالربط في Elasticsearch ، في الورشة الأساسيّة 1.
  • RETURN p ليست إلزاميّة. بدونها ، يُنشئ الاستعلام الفقّاعة ولا يعرض إلّا الحصيلة. إنّها هنا لتراها.

الخطوة 2 — إعطاؤها قيمة

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

ما يطلبه الاستعلام : اعثر على فقّاعات Personne (واحدة فقط) ، اكتب فيها nom = 'Alice'، أظهرها.

الحصيلة : Set 1 property.

انظر إلى : تحمل الفقّاعة الآن Alice كتسمية (يعرض Browser افتراضيًّا أوّل خاصيّة نصّيّة). اضغط عليها : nom: Alice. معادل SQL : UPDATE personne SET nom = 'Alice'.

لفهم أعمق
  • MATCH قبل SET. لتعديل فقّاعة ، يجب إيجادها أوّلًا. MATCH (p:Personne) هو SELECT … FROM personne ؛ SET هو UPDATE. تسلسل Cypher الاثنين في الجملة نفسها.
  • النصوص بين علامتي اقتباس مفردتين 'Alice' (تعمل علامات الاقتباس المزدوجة أيضًا). تُكتَب الأرقام عارية : 30، ليس '30'.
  • لا توجد خاصيّة إلّا إذا كانت لها قيمة. لا يوجد عمود nom فارغ عند بوب بعد قليل : لن يمتلك ببساطة خاصيّة ville.

الخطوة 3 — عدّة قيم دفعة واحدة

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

ما يطلبه الاستعلام : اعثر على الفقّاعة Personne التي nom فيها هو Alice، اكتب قيمتين إضافيّتين.

الحصيلة : Set 2 properties.

انظر إلى : الفقّاعة ، بالضغط عليها ، تُظهر nom: Alice، age: 30، ville: Montréal. الـ {nom: 'Alice'} بين الأقواس المُعقَّفة مرشِّح : « تلك التي nom فيها يساوي Alice »، وهو WHERE nom = 'Alice' في SQL. لا يُغيّر شيئًا هنا (Personne واحدة فقط) ، لكنّه يصبح لا غنى عنه من الخطوة التالية.

الخطوة 4 — إنشاء عقدة بقيمها دفعة واحدة

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

ما يطلبه الاستعلام : أنشئ فقّاعة Personne تحمل فورًا nom و age.

الحصيلة : Created 1 node, set 2 properties, added 1 label.

انظر الآن إلى فقّاعتيك في جدول :

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

معاينة Table :

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

انظر إلى : null لمدينة بوب : لا يمتلك هذه الخاصيّة. في SQL ستكون خليّة فارغة في عمود ville ؛ هنا ، الخانة غير موجودة على الإطلاق. معادل SQL : SELECT nom, age, ville FROM personne.

الخطوة 5 — ربط أليس ببوب

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

ما يطلبه الاستعلام : اعثر على أليس ، سمّها a ؛ اعثر على بوب ، سمّه b ؛ أنشئ سهمًا من نوع CONNAIT ينطلق من a ويصل إلى b.

الحصيلة : Created 1 relationship، ثم Completed after … ms. لا نتيجة : لا يوجد RETURN.

انظر إلى السهم :

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

ما يطلبه الاستعلام : كلّ الأسهم ، بغضّ النظر عن نوعها ([r] بدون :TYPE)، التي تذهب من Personne إلى Personne.

انظر إلى : معاينة Graph ، فقّاعتان وسهم مكتوب عليه CONNAIT، باتّجاه أليس → بوب. إلى اليمين ، Results overview : Nodes (2)، Relationships (1) مع الرقعة CONNAIT (1). معادل SQL : INSERT INTO connait (personne_source, personne_cible) VALUES (id_alice, id_bob)، في جدول ربط كان يجب إنشاؤه قبل ذلك.

لفهم أعمق
  • تربط العلاقة فقّاعتين موجودتين بالفعل. من هنا MATCH في السطر الأوّل : بدونه ، CREATE (a)-[:CONNAIT]->(b) سيُنشئ فقّاعتين جديدتين وفارغتين و سهمًا بينهما. خطأ كلاسيكيّ.
  • يُكتَب النوع بحروف كبيرة تبعًا للاتّفاقيّة، مع _ : CONNAIT, A_SUIVI, ENSEIGNE. ليس إلزاميًّا ، إنّه ما يفعله الجميع.
  • للعلاقة دائمًا اتّجاه. (a)-[:CONNAIT]->(b) : رأس السهم على b. لا توجد علاقة بلا اتّجاه في Neo4j ؛ عند القراءة ، بالمقابل ، يمكن تجاهل الاتّجاه (الخطوة 7).

الخطوة 6 — قيمة على السهم

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

ما يطلبه الاستعلام : اعثر على سهم CONNAIT الذي يذهب من أليس إلى بوب ، سمّه r، اكتب عليه depuis = 2024.

الحصيلة : Set 1 property.

اقرأ كلّ شيء في جدول :

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

انظر إلى : type(r) يعطي نوع السهم ؛ r.depuis خاصيّته ، بالضبط كـ p.nom لفقّاعة. تُعامَل العلاقة كعقدة : نجدها ، نُسمّيها ، نقرأ ونكتب فيها. الفقّاعات بلا اسم مؤقّت ، (:Personne {…})، هي تلك التي لا تحتاجها بعد ذلك : يمكنك حذف الحرف.

الخطوة 7 — للسهم اتّجاه

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

ما يطلبه الاستعلام : من يعرف بوب؟ الفقّاعات x في نهاية سهم CONNAIT ينطلق من بوب.

text
(no changes, no records)

انظر إلى : صفر سطر. السهم الوحيد يذهب من أليس إلى بوب ؛ لا سهم ينطلق من بوب. الآن بدون رأس السهم :

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

-[:CONNAIT]- بدون > : « سهم CONNAIT بين بوب و x، في اتّجاه أو في الآخر ». تظهر أليس. احتفظ بـ : عند الكتابة ، الاتّجاه إلزاميّ ؛ عند القراءة ، تختار أخذه بالحسبان أو لا. هذا ما يجعل « من مرتبط بمن » قصيرًا جدًّا للكتابة في Cypher.

الخطوة 8 — ربط فقّاعتك بالرسم البيانيّ الحقيقيّ

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

ما يطلبه الاستعلام : اعثر على أليس ، اعثر على الدورة C0001 من مجموعة البيانات ، أنشئ سهمًا AIME من الأولى إلى الثانية.

الحصيلة : Created 1 relationship.

ارتبطت أليس الآن بفقّاعة كانت موجودة قبلك. انظر إلى كلّ ما ينطلق منها :

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

انظر إلى : سطران ، نوعان من السهم ، نوعان من الهدف. labels(x)[0] يعطي علامة الفقّاعة في النهاية ؛ coalesce(x.nom, x.titre) يأخذ nom إن وُجد ، وإلّا titre (لبوب nom، للدورة titre). هذا جدولك-تسليمتك : احتفظ به. في SQL ، سيكون مطلوبًا ربط لكلّ جدول هدف و UNION ؛ هنا ، سطر واحد فقط من MATCH.

انتقل إلى معاينة Graph على MATCH (a:Personne {nom: 'Alice'})-[r]->(x) RETURN a, r, x : أليس في المركز ، بوب على جانب ، الدورة على الآخر ، بلونين (واحد لكلّ علامة).

الخطوة 9 — العدّ

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

872 + فقّاعتاك. حسب العلامة :

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

انظر إلى : سطر سادس ، Personne 2. في طرفيّة ، يعرض .\labo.ps1 etat أو ./labo.sh etat الآن ✔ Neo4j répond — nœuds : 874. يعدّ النصّ البرمجيّ وNeo4j Browser الشيء نفسه بمسارين : هذا التحقّق المتبادل من التطبيق الموجّه ، مُطبَّق على ما أنشأته أنت بنفسك.

الخطوة 10 — التنظيف ، ومواجهة الخطأ الذي يشرح كلّ شيء

جرّب أوّلًا الحذف السطحيّ :

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

انظر إلى : إطار أحمر ، لم يُحذَف شيء. يرفض Neo4j مسح فقّاعة لديها أسهم متبقّية : سهم بلا فقّاعة في النهاية لن يكون له معنى. الرقم node<874> هو المعرّف الداخليّ لأوّل فقّاعة حاول حذفها ؛ عندك يختلف. هذا معادل انتهاك مفتاح أجنبيّ في SQL ، بفرق واحد : هنا ، العلاج في كلمة واحدة.

cypher
MATCH (p:Personne) DETACH DELETE p

ما يطلبه الاستعلام : اعثر على فقّاعات Personne، فُكّها (احذف أسهمها) ، ثم احذفها.

الحصيلة : Deleted 2 nodes, deleted 2 relationships.

عقدتان ، علاقتان : CONNAIT و AIME. لم يُلمَس الدرس C0001، اختفى فقط السهم الذي كان يقود إليه. تحقّق :

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

يُعيد MATCH (n) RETURN labels(n)[0] AS label, count(*) ORDER BY label خمسة أسطر من جديد ، بلا Personne، ويقول etat nœuds : 872.

لفهم أعمق
  • DETACH DELETE هي الحركة الطبيعيّة لحذف عقدة في Neo4j ؛ DELETE وحدها لا تُستخدَم إلّا لعقدة تعرف أنّها معزولة ، أو لحذف علاقة : MATCH ()-[r:AIME]->() DELETE r يمحو السهم ويحتفظ بالفقّاعات.
  • ستكتب الوحدة 6 MERGE عوض CREATE : « أنشئ إذا لم يكن موجودًا ، وإلّا أعِد استخدامه ». هذا ما يسمح لنصّ charger-graphe البرمجيّ بإعادة التنفيذ دون تكرار. مع CREATE، إعادة تشغيل الخطوة 4 كانت ستصنع بوب ثانيًا.
  • لم يبقَ شيء مما فعلته. رجع الرسم البيانيّ إلى حالته المرجعيّة ، بالضبط كما بعد DELETE pratique-mini في الورشة الأساسيّة 1. اعتد على ذلك : ما تُنشئه للتعلّم ، تحذفه في النهاية.

التحقّق النهائيّ

  • أنشأت عقدة بعلامة واحدة فقط ورأيت أنّها بلا خاصيّة.
  • أعطيتها nom، ثم age و ville، بـ SET، بعد إيجادها أوّلًا بـ MATCH.
  • أنشأت بوب بقيمه في استعلام واحد ، ورأيت null حيث تنقص خاصيّة.
  • أنشأت CONNAIT بين فقّاعتين موجودتين ، وضعت depuis عليها ، وقرأت type(r) و r.depuis في جدول.
  • تعرف قول لماذا (Bob)-[:CONNAIT]->(x) لا يُعيد شيئًا و (Bob)-[:CONNAIT]-(x) يُعيد أليس.
  • ربطت أليس بالدورة C0001 واحتفظت بالجدول ذي السطرين من الخطوة 8 كتسليمة.
  • رأيت الخطأ Cannot delete node … because it still has relationships، ثم أعطى DETACH DELETE Deleted 2 nodes, deleted 2 relationships.
  • MATCH (n) RETURN count(n) و etat يقولان 872.

إذا عطّلت

عرض الحالات المتكرّرة
  • Invalid input, expected … بالأحمر → خطأ صياغة. الأسباب الثلاثة الأكثر شيوعًا في القاعة : قوس ( مُغلَق بقوس معقَّف ]، رأس سهم مُنسيّ عند الكتابة (-[:CONNAIT]- في CREATE مرفوض : Only directed relationships are supported in CREATE)، علامة اقتباس مُنسيّة حول نصّ.
  • Variable `r` not defined (أو a، p…) → تستخدم حرفًا في SET أو RETURN لم يُقدّمه أيّ MATCH أو CREATE من نفس الاستعلام. مثال : SET r.depuis = 2024 بينما كُتب السهم -[:CONNAIT]-> بدون r. أضف الحرف حيث يظهر الكائن : -[r:CONNAIT]->.
  • أنشأت الخطوة 5 فقّاعات فارغة إضافيّة (Created 2 nodes, created 1 relationship، ويعرض Results overview * (4)) → كتبت CREATE (a)-[:CONNAIT]->(b) بدون MATCH قبله مباشرة ، أو بفاصلة منقوطة بين السطرين. كانا a و b مجهولين : لا يحتجّ Neo4j ، يخترعهما ، بلا علامة ولا خاصيّة. نظّف : MATCH (n) WHERE size(labels(n)) = 0 DETACH DELETE n، ثم أعِد الخطوة 5 في استعلام واحد.
  • أليس أو بوب مكرّران (MATCH (p:Personne) RETURN count(p) يُعيد أكثر من 2) → أعدت تشغيل CREATE. MATCH (p:Personne) DETACH DELETE p وأعِد من الخطوة 1 ؛ ستُعلّمك الوحدة 6 MERGE، الذي يتجنّب ذلك.
  • Cannot delete node … because it still has relationships → هذه الخطوة 10 ، مقصودة. DETACH DELETE.
  • تعرض الفقّاعة رقمًا أو <id> عوض Alice → لا يعرف Browser بعد أيّ خاصيّة يعرض. اضغط على الرقعة Personne تحت الرسم البيانيّ واختر nom كتسمية ؛ أو اقرأ في معاينة Table.
  • يقول etat دائمًا nœuds : 872 بعد إنشاءاتك → أنت متّصل بقاعدة أخرى أو Neo4j آخر (neo4j مثبَّت محليًّا على المنفذ 7687؟). في Browser ، يجب أن يقول الشريط في الأعلى neo4j://localhost:7687 و Database: neo4j ؛ وإلّا ، أغلق Neo4j الآخر أو غيّر منفذه.
  • Neo.ClientError.Security.Unauthorized → كلمة سرّ مرفوضة. هي aiopsatlas2026 ؛ إذا غيّرتها وفقدتها ، ./labo.sh reinitialiser ثم demarrer و charger-graphe (تُعاد بيانات الحزمة كما هي).