- Meski UI hierarkis tampak diperlukan, hal pertama yang perlu diperiksa adalah apakah datanya benar-benar harus memiliki relasi induk-anak, atau cukup terlihat seperti itu saja
- Jika tidak membutuhkan tree sungguhan, struktur yang terlihat di layar dapat direpresentasikan hanya dengan urutan sortir absolut dari seluruh daftar dan nilai
indent, bukan ID induk - Editor game Hiss mengurutkan nama seperti
banana.eat, lalu menampilkan bagian setelah titik (.) dengan indentasi, sehingga membuat UI yang tampak seperti namespace - Pendekatan ini lebih mirip pengeditan ala pengolah kata, di mana pengguna memindahkan item ke atas/bawah serta mengindentasi/mengurangi indentasi, sehingga mengurangi beban struktur data tree
- Jika relasi antaritem memang harus dicari atau dipertahankan secara nyata, diperlukan model tree sungguhan, bukan indentasi atau trik simbol dalam string
Daftar yang terlihat seperti tree, bukan tree
- Saat mencoba menampilkan daftar dinamis seperti
FoodanBarsebagai tree view dalam aplikasi, biasanya yang terbayang adalah struktur yang menghubungkan setiap item ke item induknya - Dalam basis data relasional, misalnya, ID induk dapat disimpan dalam kolom
parentparentmilikFooadalahnullparentmilikFoo 1adalahFooparentmilikFoo 1.aadalahFoo 1
- Untuk mengambil data tree seperti ini dengan SQL, mungkin diperlukan pendekatan seperti recursive CTE
- Namun pada banyak daftar, tampilan yang tertata agar mudah dilihat manusia bisa lebih penting daripada relasi yang sebenarnya
Cara menyimpan nilai indentasi sebagai data
- Jika relasi induk-anak sungguhan tidak diperlukan, daftar dapat disimpan hanya dengan field berikut
idsortindentname
sortmerepresentasikan urutan absolut seluruh daftar, bukan urutan internal di dalam subitemindentsecara langsung merepresentasikan jumlah ruang yang ditempatkan di depan item, sehingga rendering layar menjadi sederhana- UI pengeditan juga bisa menjadi lebih sederhana dibanding manipulasi tree
- Pengguna dapat memindahkan item ke atas dan ke bawah
- Pengguna dapat mengindentasi atau mengurangi indentasi item
- Jika perlu, aturan sederhana untuk memaksakan indentasi yang benar dapat ditambahkan
- Hasilnya, pengalaman ini lebih mirip mengedit daftar di pengolah kata daripada memanipulasi langsung struktur data ala buku teks ilmu komputer
Namespace palsu berbasis titik (.) di Hiss
- Editor game petualangan teks Hiss menampilkan nama seperti
banana,banana.eat, danbanana.peeldi UI seolah-olah berbentuk hierarki - Ini bukan implementasi fitur namespace sungguhan di HissScript
- Cara implementasinya sederhana
- Mengurutkan nama objek secara alfabetis
- Jika ada titik (
.) dalam nama, bagian depannya dipotong - Bagian yang tersisa ditampilkan dengan indentasi
- Logika inti dalam kode contoh juga mengikuti alur yang sama
- Mengurutkan
things.keys - Untuk setiap nama yang memiliki titik, menambahkan indentasi lalu menghapus bagian sebelum titik sebelum ditampilkan
- Jika tidak ada titik, menampilkan nama apa adanya
- Mengurutkan
- Setelah itu, beberapa baris pemeriksaan ditambahkan untuk memastikan apakah item “induk” dengan prefiks yang diberikan memang ada
- Nesting dengan kedalaman arbitrer juga dapat ditambahkan, tetapi saat ini menunggu sampai benar-benar diperlukan
- UI yang tampak seperti namespace ini penting bagi orang yang merapikan game, tetapi tidak memiliki makna khusus bagi editor game maupun pemain
- Nama yang mengandung titik tetap hanyalah nama
- Bagian yang tampak seperti namespace hanya berperan menjaga nama tetap unik
Contoh mirip tree yang ditangani sebagai daftar datar
- Dave Long mengusulkan cara menyimpan path dan informasi dalam daftar datar sebagai “tree nyata berteknologi rendah”
- Ini merupakan insight yang mirip dengan contoh
banana.eat - Kita bisa membayangkan daftar path seperti output
findberikut./foo/zonk./foo/bonk./bar/boop/bop./bar/boop/bleep
- Jika perlu traversal depth-first, cukup urutkan path secara leksikografis
- Jika perlu traversal breadth-first, path dapat dibalik berdasarkan pemisah path, item kosong ditambahkan untuk menyamakan kedalaman, lalu diurutkan
- Contoh ini bertujuan menunjukkan konsep; dalam praktiknya, pendekatan yang lebih alami adalah memecah baris berdasarkan pemisah lalu memprosesnya sebagai array
- Secara umum daftar datar mudah ditangani, dan bila memungkinkan pendekatan yang disukai adalah memasukkan item ke dalam plain old lists
Analogi scrapbook di lantai
- Dalam pekerjaan scrapbook pribadi, foto, catatan, kartu pos, tiket, dan sebagainya dapat disebar di lantai lalu dikelompokkan
- Bagi manusia, relasi kelompoknya tampak jelas, tetapi lantai itu sendiri tidak memiliki perangkat fisik yang memaksakan relasi tersebut
- Inti analogi ini adalah bahwa relasi yang direpresentasikan dan relasi struktural yang sebenarnya bisa berbeda
- Daftar UI juga demikian: penataan yang bagi manusia tampak seperti hierarki belum tentu berarti hierarki nyata dalam model data internal
Kapan tree sungguhan diperlukan
- Pendekatan berbasis indentasi atau simbol dalam string harus banyak disesuaikan dengan konteks, dan dalam konteks pemrograman umum kemungkinan akan dianggap sebagai hack
- Jika relasi antaritem benar-benar perlu diketahui, gunakan struktur tree sungguhan yang sesuai dengan model data, seperti ID induk atau tabel join induk-anak
- Jika membutuhkan tingkat organisasi setara lemari arsip fisik dan folder, seperti saat mengklasifikasikan proyek riset berskala besar, “cara lantai” tidak cocok
- Jika sebuah proyek nantinya benar-benar perlu mengetahui relasi antaritem, meniru struktur dengan indentasi atau jumlah simbol di dalam string dapat menjadi jalan yang menyakitkan sepanjang usia proyek dan masa pemeliharaannya
1 komentar
Komentar di Hacker News
Cara pertama, yaitu cara yang terlihat seperti “tentu saja cuma cara ini”, disebut adjacency list
Cara kedua yang “jauh lebih sederhana” rasanya belum pernah saya lihat sebelumnya, dan memang punya kekurangan yang jelas, tetapi dalam beberapa kasus tampaknya sudah cukup
Cara ketiga, “namespacing”, disebut materialized path, dan ada juga nested sets sebagai cara lain untuk merepresentasikan tree: https://www.ibase.ru/files/articles/programming/dbmstrees/sq...
Dulu, ketika orang masih menangani basis data relasional secara serius, semua ini adalah hal yang sudah dikenal luas; misalnya ada tulisan seperti http://www.dbazine.com/oracle/or-articles/tropashko4/
Sekarang rasanya seperti pengetahuan yang terlupakan
Saat sedang mencoba memahami berbagai sisi masalahnya sendiri, rasanya sangat sulit menemukan nama lama untuk konsep itu
Akhirnya semua logika untuk menampilkan tree ditangani di kode, padahal dengan basis data relasional modern dan beberapa CTE saja, banyak use case bisa ditangani secara elegan dan nyaris gratis; sayang sekali
https://www.oreilly.com/library/view/joe-celkos-trees/978155...
Postgres punya tipe data ltree dan operator pencarian yang bekerja secara native seperti ini: https://www.postgresql.org/docs/current/ltree.html
Misalnya, masukkan seperti
CREATE TABLE test (path ltree);,INSERT INTO test VALUES ('Top');,INSERT INTO test VALUES ('Top.Science');,INSERT INTO test VALUES ('Top.Science.Astronomy');lalu dengan
SELECT path FROM test WHERE path <@ 'Top.Science';Anda bisa menemukanTop.SciencedanTop.Science.AstronomyPada contoh di atas, sekalipun record
Top.Sciencedihapus, recordTop.Science.Astronomytidak ikut terpotongLabel pada nilai ltree mengisyaratkan tree logis melalui materialized path, tetapi tidak memaksa keberadaan record untuk semua node induk yang tersirat
Tergantung aplikasinya, ini bisa persis perilaku yang diinginkan, atau justru kebalikannya. Jika yang terakhir, Anda perlu menyediakan mekanisme terpisah untuk menjaga integritas
/sebagai delimiter untuk menyimpan path file[1] https://learn.microsoft.com/en-us/sql/relational-databases/h...
Namun ada kekhawatiran bahwa indeks JSON mungkin tidak bekerja sebaik indeks ltree
Masalahnya di sini adalah nilai dalam struktur biasanya bukan pada tree untuk tampilan, melainkan pada hierarki data
Kemungkinan besar Anda akan melakukan hal-hal seperti menelusuri data, menampilkan relasi, atau mengurutkan ulang
Menaruh informasi visual dalam struktur data basis data terasa berbahaya dan berpandangan pendek
Apakah jawabannya, “Tidak, itu tidak mungkin”?
Ada alasan mengapa YAGNI menjadi heuristik desain yang terkenal. “Anggap selalu akan dibutuhkan” itu tidak tepat
Hanya saja bukan disimpan di kolom khusus dengan tipe data yang dioptimalkan, melainkan ditempelkan di depan string data
Mungkin bukan angka dan mungkin bukan kolom ID, tetapi tetap merupakan identifier yang menunjuk ke nilai lain yang diharapkan, jadi perubahan format tidak membuatnya bukan ID induk
Tentu saja harus dijamin agar indentasi keliru seperti anak tanpa induk tidak tersimpan
Jadi menurut saya cara termudah adalah menyimpannya terlebih dahulu sebagai urutan/kedalaman, lalu saat fitur yang diperlukan diimplementasikan, migrasikan ke model induk/anak
Namun, “indentasi” sebaiknya didefinisikan lebih abstrak sebagai kedalaman dalam tree, bukan jumlah spasi yang dirender. Ini memudahkan menemukan data yang salah, memudahkan migrasi berikutnya, dan memberi fleksibilitas rendering per pengguna seperti
/bertingkat, tab, 8 spasi, 4 spasi, atau 1 spasistruct item_t { char key[255]; char display_value[255]; }, dan key memiliki pemisah path yang konsisten sepertia/b/c, menemukan induk dan anak sangat mudahDalam kasus terburuk cukup lakukan pencarian linear pada array, dan jika sudah terurut, cukup lihat item sebelumnya sampai mencapai induk
Saya pernah memulai perusahaan yang punya banyak data berbentuk tree. Mengubah struktur tree menjadi daftar berindentasi bisa dilakukan dalam waktu O(n)
Itu salah satu pertanyaan wawancara saat itu, dan ada berbagai cara menyimpannya di beberapa basis data SQL agar sebagian tree bisa diambil dan dirender dengan cepat tanpa query rekursif
Setelah memahami konsep seperti ini, menyimpan data dengan benar sebagai tree punya jauh lebih banyak keunggulan dibanding indentasi semacam ini
“Salah satu cara mengambil data berstruktur tree dari basis data relasional dengan query SQL adalah memakai recursive CTE (Common Table Expressions), yang sama menyenangkannya dengan namanya”
CTE, bahkan termasuk recursive CTE, bukan sesuatu yang menakutkan, dan saya jamin setelah terbiasa justru benar-benar menyenangkan
Untuk merakit path node dengan kedalaman hierarki d, waktu untuk mendapatkan hasil query setidaknya menjadi d kali lebih lambat
Keuntungannya adalah operasi pengeditan tree menjadi murah, tetapi itu jauh lebih jarang terjadi dibanding pembacaan
Dari gagasan bahwa “orang sering kali sebenarnya tidak menginginkan atau membutuhkan tree, mereka hanya membutuhkan sesuatu yang terlihat seperti tree,” terlihat perbedaan antara HN dan Reddit
Di HN, komentar anak adalah
nextSiblingdari komentar induk, dan dibuat tampak seperti tree dengan menambahkan 1 ke nilai indentasi induknyaDi Reddit, setidaknya di old.reddit.com, komentar anak benar-benar disarangkan di dalam komentar induk. Saya tidak tahu soal situs yang baru
Semua operasi atas data akan menjadi kekacauan rumit yang harus menyimpulkan struktur tree lalu menerjemahkannya kembali ke format tree implisit
Ide utama tulisan itu sederhana: gunakan struktur yang sesuai dengan masalah
Namun menurut saya narasinya keliru. Untuk mengambil tree dari basis data, CTE tidak selalu diperlukan; kita bisa mengambil daftar datar lalu membangun tree secara lokal. Untuk manipulasi berikutnya pun kemungkinan besar memang harus begitu
Dengan logika yang sama, orang yang memakai basis data relasional untuk menyimpan daftar juga bisa disuruh menyimpannya di file teks. Untuk apa membayar biaya latensi jaringan?
Sebaliknya, struktur yang diusulkan tidak bekerja baik jika harus memindahkan cabang dan mengubah kedalaman pada tree yang cukup besar, karena biayanya linear
Seharusnya niatnya dinyatakan sejak awal. Jangan menjelaskan tiga contoh lalu membatalkannya di kesimpulan dengan “kalau butuh tree, pakailah tree.” Namun kalau itu ditaruh di depan, tulisannya pasti jauh kurang clickbait
Beberapa tahun lalu saya mendapat pencerahan serupa tentang OpenGL. Yang perlu digambar bukanlah dunia objek 3D hierarkis, melainkan daftar segitiga terurut
Pemikiran itu menyalakan semacam sakelar di kepala saya, dan berbagai optimisasi menjadi sangat mudah
Bahkan pada game dengan hierarki entitas yang kompleks, saat dimasukkan ke render queue, sering kali harus diratakan karena alasan seperti pengurutan transparansi
“Daftar datar benda-benda” juga merupakan dasar ECS/DOD
Ada satu buku utuh yang membahas pekerjaan semacam ini di basis data
https://www.oreilly.com/library/view/joe-celkos-trees/978155...
Cara lain membuat tree palsu adalah menyimpan blob JSON
Jika data hanya memiliki relasi internal, ini bisa lebih mudah daripada berusaha menjaga nomor urut tetap unik dan berurutan