Setiap fakta pada sebuah domain dapat ditempatkan di salah satu dari tiga tempat dalam graf Neo4j, yaitu node, relasi, dan properti. Topik ini membahas kriteria praktis penempatan tersebut, tiga pertanyaan pemandu yang memeriksa setiap keputusan, serta satu ranah kecil yang dimodelkan dengan dua cara sekaligus. Ranah contohnya adalah piket kelas dengan tiga orang dan tiga jenis tugas, dan seluruh contoh dijalankan pada server Neo4j beserta keluaran aslinya. Setelah membacanya, keputusan penempatan fakta baru dapat diambil dengan alasan yang jelas, dan model lama dapat diubah secara terkendali ketika pertanyaan baru muncul.
Tiga tempat bagi sebuah fakta#
Model property graph (graf berproperti) menyediakan tiga tempat bagi sebuah fakta, dan mutu pemodelan ditentukan oleh ketepatan penempatannya. Node menampung hal atau entitas yang berdiri sendiri dan menjadi sasaran pencarian, misalnya satu orang, satu buku, atau satu tugas piket. Relasi menampung keterhubungan antar entitas, diberi nama berpola kata kerja atau kata hubung, dan memiliki arah yang menentukan pembacaan polanya. Properti menampung keterangan milik satu entitas atau satu peristiwa tunggal, dengan nilai tunggal yang bertipe seperti angka, string, tanggal, atau boolean.
Ketiga tempat itu bekerja dalam pembagian tugas yang jelas, dan tabel berikut memetakannya ke ranah piket yang digunakan sepanjang topik ini.
| Tempat | Tugasnya | Contoh di ranah piket |
|---|---|---|
| Node | Hal yang berdiri sendiri dan menjadi sasaran penyaringan | :Orang dan :Tugas |
| Relasi | Keterhubungan antar entitas | :MELAKSANAKAN dari orang menuju tugas |
| Properti | Keterangan milik satu entitas atau satu peristiwa | nama, tanggal, dan status |
Properti dapat pula melekat pada relasi, dan penggunaannya diperagakan pada model kedua. Penempatan yang berbeda atas data yang sama bukan kesalahan sintaks, melainkan keputusan desain yang berbeda konsekuensi kuerinya. Perpindahan fakta antar tempat juga hal yang wajar, karena model bukan keputusan sekali jadi melainkan susunan yang menyesuaikan diri dengan pertanyaan. Dua model di bawah menyimpan fakta piket yang sama, dan perbedaannya baru terlihat ketika kueri ditulis.
Tiga pertanyaan pemandu#
Penempatan fakta dapat diperiksa dengan tiga pertanyaan pemandu berikut.
- Apakah fakta perlu disaring atau dihitung sebagai kelompok tersendiri?
- Apakah fakta punya keterhubungan sendiri terhadap entitas lain?
- Apakah fakta masih bermakna tanpa induknya?
Fakta yang perlu disaring atau dihitung sebagai kelompok tersendiri layak menjadi node berlabel, karena pencarian pada label membuat pola kueri langsung menunjuk kelompok yang dimaksud. Fakta yang punya keterhubungan sendiri terhadap entitas lain wajib menjadi node, karena hanya node yang dapat menjadi ujung relasi, sedangkan relasi tidak dapat dihubungkan oleh relasi lain. Keterangan yang melekat pada satu induk, seperti tanggal pada sebuah peristiwa, cukup menjadi properti yang letaknya mengikuti induk tersebut.
Ketiga pertanyaan menempatkan fakta di dalam graf yang sudah dipilih. Pertanyaan yang lebih mendasar tetap dinyatakan terbuka pada topik ini, yaitu apakah sekumpulan data memang layak disimpan sebagai graf. Keputusan itu baru dapat diambil setelah daftar kueri yang akan diajukan diketahui, karena model selalu melayani pertanyaan, bukan sebaliknya.
Model pertama: piket sebagai properti#
Cara pertama menyimpan peristiwa piket sebagai properti pada node orang. Setiap node :Orang membawa properti piketTanggal dan piketStatus, sehingga satu orang memuat satu peristiwa saja. Nilai tanggal dinyatakan dengan fungsi date() yang menerima string tanggal. Skrip berikut membangun ketiga node dengan MERGE sehingga aman dijalankan berulang, sedangkan penyaring WHERE o.piketTanggal IS NOT NULL menjaga keluaran tetap pada node yang membawa properti piket.
MERGE (o:Orang {nama: "Doni"})
SET o.piketTanggal = date("2026-11-02"), o.piketStatus = "selesai";
MERGE (o:Orang {nama: "Endang"})
SET o.piketTanggal = date("2026-11-03"), o.piketStatus = "selesai";
MERGE (o:Orang {nama: "Fita"})
SET o.piketTanggal = date("2026-11-04"), o.piketStatus = "selesai";
MATCH (o:Orang)
WHERE o.piketTanggal IS NOT NULL
RETURN o.nama AS nama, o.piketTanggal AS tanggal, o.piketStatus AS status
ORDER BY nama;nama, tanggal, status
"Doni", 2026-11-02, "selesai"
"Endang", 2026-11-03, "selesai"
"Fita", 2026-11-04, "selesai"Kueri pencarian langsung terjawab. Kueri berikut mencari orang yang bertugas pada tanggal 2 November 2026.
MATCH (o:Orang)
WHERE o.piketTanggal = date("2026-11-02")
RETURN o.nama AS nama;nama
"Doni"Model ini cukup selama setiap orang hanya melakukan satu peristiwa piket. Pola kuerinya pendek dan tidak ada konsep tambahan yang perlu dipahami. Konsekuensinya muncul ketika satu orang mendapat perintah piket kedua, sebab tidak ada tempat kedua bagi peristiwa pada node yang sama.
Keterbatasan ketika pertanyaan baru muncul#
Model properti diuji ketika Endang mendapat perintah piket kedua, yaitu menyiram tanaman pada tanggal 5 November 2026 dengan status tertunda. Struktur yang tersedia hanya memuat satu pasang nilai, sehingga satu-satunya jalan tanpa mengubah model adalah menimpa nilai lamanya. Kueri berikut melakukan penimpaan itu.
MATCH (o:Orang {nama: "Endang"})
SET o.piketTanggal = date("2026-11-05"), o.piketStatus = "tertunda"
RETURN o.nama AS nama, o.piketTanggal AS tanggal, o.piketStatus AS status;nama, tanggal, status
"Endang", 2026-11-05, "tertunda"Nilai baru tersimpan, tetapi peristiwa tanggal 3 November hilang bersama penimpaannya. Kueri pencarian pada tanggal tersebut kini mengembalikan nol tanpa pesan error apa pun.
MATCH (o:Orang)
WHERE o.piketTanggal = date("2026-11-03")
RETURN count(o) AS jumlah;jumlah
0Kehilangan itu bukan kerusakan data, melainkan konsekuensi penempatan fakta pada tempat yang salah. Peristiwa piket berulang untuk satu orang dan berdiri berdampingan dengan peristiwa lain, sedangkan properti hanya menampung satu nilai pada setiap kuncinya. Ketiga pertanyaan pemandu menunjukkan ketidakcocokan itu, dan jawabannya adalah perpindahan fakta ke tempat yang memuat banyak peristiwa.
Model kedua: peristiwa piket pada relasi#
Cara kedua mengangkat tugas menjadi node :Tugas dan menurunkan peristiwa piket ke relasi :MELAKSANAKAN yang menghubungkan orang dengan tugasnya. Tanggal dan status menjadi properti relasi, sesuai ketiga pertanyaan pemandu, karena peristiwa berulang pada satu orang dan keterangan itu milik peristiwanya, bukan milik orangnya. Skrip berikut membangun ketiga node :Tugas, membuat lima relasi :MELAKSANAKAN dengan MERGE, lalu mencabut properti piket dari node orang agar tidak ada dua salinan fakta yang tersisa. Peristiwa Endang pada tanggal 3 November ikut dimuat kembali karena nilai lamanya masih diketahui dari catatan.
MERGE (t:Tugas {nama: "Menyapu lantai"});
MERGE (t:Tugas {nama: "Merapikan perpustakaan"});
MERGE (t:Tugas {nama: "Menyiram tanaman"});
MATCH (o:Orang {nama: "Doni"}), (t:Tugas {nama: "Menyapu lantai"})
MERGE (o)-[r:MELAKSANAKAN {tanggal: date("2026-11-02")}]->(t)
SET r.status = "selesai";
MATCH (o:Orang {nama: "Endang"}), (t:Tugas {nama: "Menyapu lantai"})
MERGE (o)-[r:MELAKSANAKAN {tanggal: date("2026-11-03")}]->(t)
SET r.status = "selesai";
MATCH (o:Orang {nama: "Fita"}), (t:Tugas {nama: "Merapikan perpustakaan"})
MERGE (o)-[r:MELAKSANAKAN {tanggal: date("2026-11-04")}]->(t)
SET r.status = "selesai";
MATCH (o:Orang {nama: "Doni"}), (t:Tugas {nama: "Merapikan perpustakaan"})
MERGE (o)-[r:MELAKSANAKAN {tanggal: date("2026-11-04")}]->(t)
SET r.status = "selesai";
MATCH (o:Orang {nama: "Endang"}), (t:Tugas {nama: "Menyiram tanaman"})
MERGE (o)-[r:MELAKSANAKAN {tanggal: date("2026-11-05")}]->(t)
SET r.status = "tertunda";
MATCH (o:Orang)
REMOVE o.piketTanggal, o.piketStatus;
MATCH (o:Orang)-[r:MELAKSANAKAN]->(t:Tugas)
WHERE r.tanggal >= date("2026-11-02") AND r.tanggal <= date("2026-11-05")
RETURN o.nama AS orang, t.nama AS tugas, r.tanggal AS tanggal, r.status AS status
ORDER BY r.tanggal, orang;orang, tugas, tanggal, status
"Doni", "Menyapu lantai", 2026-11-02, "selesai"
"Endang", "Menyapu lantai", 2026-11-03, "selesai"
"Doni", "Merapikan perpustakaan", 2026-11-04, "selesai"
"Fita", "Merapikan perpustakaan", 2026-11-04, "selesai"
"Endang", "Menyiram tanaman", 2026-11-05, "tertunda"Lima peristiwa kini tersimpan sebagai lima relasi, termasuk dua peristiwa milik Doni dan dua milik Endang. Pertanyaan yang mustahil dijawab pada model pertama kini menjadi satu pola MATCH dengan penghitungan, yaitu berapa kali setiap tugas dilaksanakan selama periode tersebut.
Pertanyaan agregasi yang kini terjawab#
Fungsi count() menghitung relasi per tugas, dan ORDER BY mengurutkan hasilnya dari yang terbanyak. Kueri berikut menerapkannya pada seluruh relasi :MELAKSANAKAN yang terikat node :Tugas.
MATCH (o:Orang)-[r:MELAKSANAKAN]->(t:Tugas)
RETURN t.nama AS tugas, count(r) AS dilaksanakan
ORDER BY dilaksanakan DESC, tugas;tugas, dilaksanakan
"Menyapu lantai", 2
"Merapikan perpustakaan", 2
"Menyiram tanaman", 1Penyaringan status juga dilakukan pada properti relasi. Kueri berikut menampilkan seluruh peristiwa yang sudah selesai.
MATCH (o:Orang)-[r:MELAKSANAKAN]->(t:Tugas)
WHERE r.status = "selesai"
RETURN o.nama AS orang, t.nama AS tugas, r.tanggal AS tanggal
ORDER BY r.tanggal, orang;orang, tugas, tanggal
"Doni", "Menyapu lantai", 2026-11-02
"Endang", "Menyapu lantai", 2026-11-03
"Doni", "Merapikan perpustakaan", 2026-11-04
"Fita", "Merapikan perpustakaan", 2026-11-04Keluaran di atas memperlihatkan perilaku yang tidak mungkin dicapai model pertama, yaitu daftar peristiwa yang utuh meskipun satu orang melakukan lebih dari satu tugas. Satu pola MATCH yang sama melayani pertanyaan per orang, per tugas, atau per tanggal, sehingga pertanyaan baru tidak lagi menuntut perubahan model. Apabila peristiwa kelak perlu menyimpan keterangan dari lebih banyak pihak, pola node antara menjadi kelanjutan evolusinya.
Kesalahan umum#
Tiga kesalahan berikut muncul ketika kriteria penempatan dilewatkan.
- Menumpuk fakta berulang pada satu properti string. Menulis “2026-11-02, 2026-11-04” pada satu properti memaksa penyaringan dilakukan di luar basis data, karena nilai itu diperlakukan sebagai satu string tunggal dan tidak dapat dihitung per elemennya. Angkat fakta yang berulang menjadi relasi atau node sebelum pola kuerinya berkembang.
- Menempatkan keterangan peristiwa pada induk yang salah. Tanggal dan status piket yang diletakkan pada node orang membuat peristiwa berikutnya menimpa yang lama tanpa pesan apa pun, karena properti hanya tepat untuk keterangan milik satu induk. Tempatkan keterangan itu pada relasi yang mewakili peristiwanya.
- Menggunakan label sebagai nilai. Label
:Selesaidan:Tertundauntuk status melipatgandakan jumlah label dan menyembunyikan nilai yang seharusnya dapat dicari serta dihitung. Label menandai kelas entitas, sedangkan status dinyatakan sebagai properti bertipe string, sebagaimana digunakan model kedua di atas.
Ringkasan#
- Tiga tempat bagi fakta dalam graf adalah node untuk hal yang berdiri sendiri dan disaring, relasi untuk keterhubungan antar entitas, dan properti untuk keterangan milik satu entitas atau satu peristiwa.
- Tiga pertanyaan pemandu memeriksa setiap penempatan, yaitu apakah fakta perlu disaring, apakah fakta punya relasi sendiri, dan apakah fakta masih bermakna tanpa induknya.
- Fakta yang berulang tidak dapat ditampung pada satu properti tunggal, sehingga peristiwa piket pindah dari properti node ke relasi
:MELAKSANAKANdengan properti tanggal dan status. - Model berevolusi mengikuti pertanyaan baru, dan perpindahan fakta itu mengubah kueri yang mustahil menjadi satu pola
MATCHdengan penghitungan.
Referensi#
- What is graph data modeling? — Neo4j Docs — Ikhtisar pemodelan data graf yang menempatkan penetapan pertanyaan penggunaan sebagai langkah pertama sebelum struktur data ditetapkan.
- Introduction — Neo4j Docs — Pengantar Cypher sebagai bahasa kueri deklaratif di atas basis data property graph.
- SET — Neo4j Docs
— Referensi klausa
SETyang menimpa nilai properti lama dengan nilai baru tanpa menyimpan riwayatnya.