Arsitektur
ArsitekturMesin pemuatan data

Mesin pemuatan data

Gato GraphQL menggunakan komponen sisi server untuk merepresentasikan model data (bukan graf atau pohon). Mari kita lihat bagaimana ia menjalankan proses pemuatan data untuk menyelesaikan GraphQL query.

Untuk memproses data, kita harus meratakan komponen menjadi tipe (<FeaturedDirector> => Director, <Film> => Film, <Actor> => Actor), mengurutkannya sesuai kemunculannya dalam hierarki komponen (Director, kemudian Film, kemudian Actor) dan menanganinya dalam "iterasi", mengambil data objek untuk setiap tipe pada iterasinya masing-masing, seperti ini:

Menangani tipe dalam iterasi

Mesin pemuatan data server harus mengimplementasikan (pseudo-)algoritma berikut untuk memuat data:

Persiapan:

  1. Siapkan antrean kosong untuk menyimpan daftar ID dari objek-objek yang harus diambil dari database, diorganisasikan berdasarkan tipe (setiap entri akan berupa: [tipe => daftar ID])
  2. Ambil ID objek direktur unggulan, dan tempatkan di antrean di bawah tipe Director

Ulangi hingga tidak ada lagi entri di antrean:

  1. Ambil entri pertama dari antrean: tipe dan daftar ID (misalnya: Director dan [2]), dan hapus entri ini dari antrean
  2. Menggunakan objek TypeDataLoader milik tipe tersebut, jalankan satu query tunggal ke database untuk mengambil semua objek dari tipe tersebut dengan ID-ID tersebut
  3. Jika tipe memiliki field relasional (misalnya: tipe Director memiliki field relasional films bertipe Film), maka kumpulkan semua ID dari field-field tersebut dari semua objek yang diambil pada iterasi saat ini (misalnya: semua ID di field films dari semua objek bertipe Director), dan tempatkan ID-ID tersebut di antrean di bawah tipe yang sesuai (misalnya: ID [3, 8] di bawah tipe Film).

Pada akhir iterasi, kita akan telah memuat semua data objek untuk semua tipe, seperti ini:

Menangani tipe dalam iterasi

Perhatikan bagaimana semua ID untuk suatu tipe dikumpulkan, sampai tipe tersebut diproses di antrean. Jika, misalnya, kita menambahkan field relasional preferredActors ke tipe Director, ID-ID tersebut akan ditambahkan ke antrean di bawah tipe Actor, dan akan diproses bersama dengan ID-ID dari field actors dari tipe Film:

Menangani tipe dalam iterasi

Namun, jika suatu tipe telah diproses dan kemudian kita perlu memuat lebih banyak data dari tipe tersebut, maka itu adalah iterasi baru pada tipe tersebut. Misalnya, menambahkan field relasional preferredDirector ke tipe Author akan membuat tipe Director ditambahkan ke antrean sekali lagi:

Iterasi pada tipe yang berulang

Sekarang setelah kita mengambil semua data objek, kita perlu membentuknya menjadi respons yang diharapkan, mencerminkan GraphQL query. Namun, seperti yang dapat dilihat, data tidak memiliki struktur pohon yang diperlukan. Sebaliknya, field relasional berisi ID ke objek bersarang, meniru bagaimana data direpresentasikan dalam database relasional. Oleh karena itu, mengikuti perbandingan ini, data yang diambil untuk setiap tipe dapat direpresentasikan sebagai tabel, seperti ini:

Tabel untuk tipe Director:

IDnamecountryavatarfilms
2George LucasUSAgeorge-lucas.jpg[3, 8]

Tabel untuk tipe Film:

IDtitlethumbnailactors
3The Phantom Menaceepisode-1.jpg[4, 6]
8Attack of the Clonesepisode-2.jpg[6, 7]

Tabel untuk tipe Actor:

IDnameavatar
4Ewan McGregormcgregor.jpg
6Nathalie Portmanportman.jpg
7Hayden Christensenchristensen.jpg

Dengan semua data yang terorganisasi sebagai tabel, dan mengetahui bagaimana setiap tipe berhubungan satu sama lain (yaitu Director mereferensikan Film melalui field films, Film mereferensikan Actor melalui field actors), server GraphQL dapat dengan mudah mengonversi data menjadi bentuk pohon yang diharapkan:

Respons berbentuk pohon

Akhirnya, server GraphQL mengeluarkan pohon, yang memiliki bentuk respons yang diharapkan:

{
  data: {
    featuredDirector: {
      name: "George Lucas",
      country: "USA",
      avatar: "george-lucas.jpg",
      films: [
        {
          title: "Star Wars: Episode I",
          thumbnail: "episode-1.jpg",
          actors: [
            {
              name: "Ewan McGregor",
              avatar: "mcgregor.jpg",
            },
            {
              name: "Natalie Portman",
              avatar: "portman.jpg",
            }
          ]
        },
        {
          title: "Star Wars: Episode II",
          thumbnail: "episode-2.jpg",
          actors: [
            {
              name: "Natalie Portman",
              avatar: "portman.jpg",
            },
            {
              name: "Hayden Christensen",
              avatar: "christensen.jpg",
            }
          ]
        }
      ]
    }
  }
}

Menganalisis kompleksitas waktu solusi

Mari kita analisis notasi big O dari algoritma pemuatan data untuk memahami bagaimana jumlah query yang dijalankan terhadap database bertumbuh seiring bertambahnya jumlah input, guna memastikan bahwa solusi ini berkinerja baik.

Mesin pemuatan data memuat data dalam iterasi yang berkorespondensi dengan setiap tipe. Pada saat memulai suatu iterasi, ia sudah memiliki daftar semua ID untuk semua objek yang akan diambil, sehingga dapat menjalankan 1 query tunggal untuk mengambil semua data bagi objek-objek yang bersangkutan. Maka dari itu, jumlah query ke database akan bertumbuh secara linier dengan jumlah tipe yang terlibat dalam query. Dengan kata lain, kompleksitas waktu adalah O(n), di mana n adalah jumlah tipe dalam query (namun, jika suatu tipe diiterasi lebih dari sekali, maka harus ditambahkan lebih dari sekali ke n).

Solusi ini sangat berkinerja baik, jauh lebih baik dari kompleksitas eksponensial yang diharapkan dari penanganan graf, atau kompleksitas logaritmik yang diharapkan dari penanganan pohon.

Kode PHP yang diimplementasikan

Proses pemuatan data berlangsung pada fungsi getComponentData dari kelas Engine dalam paket Component Model.