Node, relasi, atau properti

Menempatkan setiap fakta pada tempat yang tepat sebagai keterampilan kelas satu.

Daftar isi

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.

TempatTugasnyaContoh di ranah piket
NodeHal yang berdiri sendiri dan menjadi sasaran penyaringan:Orang dan :Tugas
RelasiKeterhubungan antar entitas:MELAKSANAKAN dari orang menuju tugas
PropertiKeterangan milik satu entitas atau satu peristiwanama, 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.

cypher
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;
text Keluaran
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.

cypher
MATCH (o:Orang)
WHERE o.piketTanggal = date("2026-11-02")
RETURN o.nama AS nama;
text Keluaran
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.

cypher
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;
text Keluaran
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.

cypher
MATCH (o:Orang)
WHERE o.piketTanggal = date("2026-11-03")
RETURN count(o) AS jumlah;
text Keluaran
jumlah
0

Kehilangan 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.

cypher
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;
text Keluaran
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.

cypher
MATCH (o:Orang)-[r:MELAKSANAKAN]->(t:Tugas)
RETURN t.nama AS tugas, count(r) AS dilaksanakan
ORDER BY dilaksanakan DESC, tugas;
text Keluaran
tugas, dilaksanakan
"Menyapu lantai", 2
"Merapikan perpustakaan", 2
"Menyiram tanaman", 1

Penyaringan status juga dilakukan pada properti relasi. Kueri berikut menampilkan seluruh peristiwa yang sudah selesai.

cypher
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;
text Keluaran
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-04

Keluaran 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.

  1. 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.
  2. 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.
  3. Menggunakan label sebagai nilai. Label :Selesai dan :Tertunda untuk 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 :MELAKSANAKAN dengan properti tanggal dan status.
  • Model berevolusi mengikuti pertanyaan baru, dan perpindahan fakta itu mengubah kueri yang mustahil menjadi satu pola MATCH dengan penghitungan.

Referensi#