Pengindeksan untuk Panel Admin: Optimalkan Filter Teratas Dulu
Pengindeksan untuk panel admin: optimalkan filter yang paling sering diklik pengguna — status, assignee, rentang tanggal, dan pencarian teks — berdasarkan pola query nyata.

Mengapa filter panel admin melambat
Panel admin biasanya terasa cepat di awal. Anda membuka daftar, menggulir, mengklik sebuah record, lalu lanjut. Perlambatan muncul ketika orang memfilter sesuai cara mereka bekerja: "Hanya tiket terbuka", "Ditugaskan ke Maya", "Dibuat minggu lalu", "Order ID mengandung 1047". Setiap klik memicu jeda, dan daftar mulai terasa lengket.
Tabel yang sama bisa cepat untuk satu filter dan menyakitkan lambat untuk filter lain. Filter status mungkin menyentuh sebidang kecil baris dan kembali cepat. Filter "dibuat antara dua tanggal" bisa memaksa database membaca rentang besar. Filter assignee bisa baik sendiri, lalu melambat setelah Anda menggabungkannya dengan status plus pengurutan.
Indeks adalah jalan pintas yang digunakan database untuk menemukan baris yang cocok tanpa membaca seluruh tabel. Tapi indeks tidak gratis. Mereka memakan ruang, dan membuat insert serta update sedikit lebih lambat. Menambahkan terlalu banyak bisa membuat penulisan lebih lambat dan tetap tidak memperbaiki bottleneck sesungguhnya.
Alih-alih mengindeks semuanya, prioritaskan filter yang:
- sering dipakai terus-menerus
- menyentuh banyak baris
- menciptakan penantian yang terlihat
- bisa diperbaiki dengan aman menggunakan indeks kecil yang cocok
Fokus ini sengaja sempit. Keluhan performa pertama pada daftar admin hampir selalu datang dari empat tipe filter yang sama: status, assignee, rentang tanggal, dan field teks. Setelah Anda mengerti mengapa ini berperilaku berbeda, langkah selanjutnya jelas: lihat pola query nyata, tambahkan indeks terkecil yang cocok dengan mereka, dan verifikasi Anda memperbaiki jalur lambat tanpa menciptakan masalah baru.
Pola query di balik kerja admin nyata
Panel admin jarang lambat karena satu laporan raksasa. Mereka melambat karena beberapa layar dipakai sepanjang hari, dan layar-layar itu menjalankan banyak query kecil berulang-ulang.
Tim operasi biasanya bekerja di beberapa antrean kerja: tiket, pesanan, pengguna, persetujuan, permintaan internal. Di halaman-halaman ini, filter berulang:
- Status, karena mencerminkan alur kerja (New, Open, Pending, Done)
- Assignee, karena tim butuh "item saya" dan "belum ditugaskan"
- Rentang tanggal, karena selalu ada yang bertanya "apa yang terjadi minggu lalu?"
- Pencarian, untuk lompat ke item yang sudah dikenal (nomor order, email) atau memindai teks (catatan, preview)
Pekerjaan database bergantung pada niat:
- Browse terbaru adalah pola scanning. Biasanya seperti "tampilkan item terbaru, mungkin dipersempit ke status, diurutkan berdasarkan created time" dan dipaginasi.
- Menemukan item spesifik adalah pola lookup. Admin sudah punya ID, email, nomor tiket, atau referensi, dan mengharapkan database langsung lompat ke sejumlah kecil baris.
Panel admin juga menggabungkan filter dengan cara yang dapat diprediksi: "Open + Unassigned", "Pending + Assigned to me", atau "Completed dalam 30 hari terakhir". Indeks bekerja terbaik ketika mereka cocok dengan bentuk query nyata tersebut, bukan ketika mereka hanya cocok dengan daftar kolom.
Jika Anda membangun alat admin di AppMaster, pola ini biasanya terlihat hanya dengan melihat layar daftar yang paling sering dipakai dan filter defaultnya. Itu membuat lebih mudah mengindeks apa yang benar-benar mendorong pekerjaan sehari-hari, bukan apa yang terlihat bagus di atas kertas.
Cara memilih apa yang diindeks terlebih dahulu
Perlakukan pengindeksan seperti triase. Jangan mulai dengan mengindeks setiap kolom yang muncul di dropdown filter. Mulailah dengan beberapa query yang berjalan terus-menerus dan paling mengganggu.
Temukan filter yang benar-benar dipakai orang
Mengoptimalkan filter yang tidak disentuh orang adalah usaha yang sia-sia. Untuk menemukan jalur panas nyata, gabungkan sinyal:
- Analitik UI: layar mana yang paling banyak dilihat, filter mana yang paling sering diklik
- Log database atau API: query yang paling sering dan beberapa persen terlambat
- Masukan internal: "pencarian lambat" biasanya menunjuk ke satu layar spesifik
- Daftar landing default: apa yang berjalan saat admin membuka panel
Di banyak tim, tampilan default itu seperti "Open tickets" atau "New orders". Itu berjalan setiap kali seseorang refresh, ganti tab, atau kembali setelah membalas.
Kelompokkan query berdasarkan bentuk, bukan nama field
Sebelum menambahkan indeks, kelompokkan query umum berdasarkan bagaimana mereka berperilaku. Sebagian besar query daftar admin jatuh ke beberapa bucket:
- Equality filters:
status = 'open',assignee_id = 42 - Range filters:
created_atantara dua tanggal - Sorting dan pagination:
ORDER BY created_at DESCdan ambil halaman 2 - Text lookups: exact match (order number), prefix match (email starts with), atau contains search
Tuliskan bentuk tiap layar paling atas, termasuk WHERE, ORDER BY, dan pagination. Dua query yang tampak mirip di UI bisa berperilaku sangat berbeda di database.
Pilih batch kecil pertama
Mulailah dengan satu target prioritas: query daftar default yang dimuat pertama. Lalu pilih 2 atau 3 query frekuensi tinggi lagi. Biasanya itu cukup untuk memangkas delay terbesar tanpa mengubah database Anda jadi museum indeks.
Contoh: tim support membuka daftar Tickets yang difilter ke status = 'open', diurutkan berdasarkan terbaru, dengan opsi assignee dan rentang tanggal. Optimalkan kombinasi persis itu dulu. Setelah cepat, lanjut ke layar berikutnya berdasarkan penggunaan.
Mengindeks filter status tanpa berlebihan
Status adalah salah satu filter pertama yang orang tambahkan, dan salah satu yang paling mudah diindeks dengan cara yang tidak membantu.
Kebanyakan field status memiliki kardinalitas rendah: hanya beberapa nilai (open, pending, closed). Indeks paling membantu ketika bisa mempersempit hasil ke irisan kecil tabel. Jika 80%–95% baris berbagi status yang sama, indeks pada status saja seringkali tidak banyak mengubah. Database masih harus membaca potongan besar baris, dan indeks menambah overhead.
Anda biasanya merasakan manfaat ketika:
- satu status itu jarang (misalnya, escalated)
- status digabungkan dengan kondisi lain yang membuat hasil kecil
- status plus pengurutan cocok dengan tampilan daftar yang umum
Pola umum adalah "tunjukkan item open, terbaru dulu." Dalam kasus itu, mengindeks filter dan sort bersama sering mengalahkan mengindeks status sendiri.
Kombinasi yang cenderung memberi manfaat pertama:
status + updated_at(filter berdasarkan status, urut berdasarkan perubahan terbaru)status + assignee_id(tampilan antrean kerja)status + updated_at + assignee_id(hanya jika tampilan persis itu sering dipakai)
Partial index adalah kompromi bagus ketika satu status mendominasi. Jika "open" adalah tampilan utama, indeks hanya baris open. Indeks tetap lebih kecil, dan biaya tulis lebih rendah.
-- PostgreSQL example: index only open rows, optimized for newest-first lists
CREATE INDEX CONCURRENTLY tickets_open_updated_idx
ON tickets (updated_at DESC)
WHERE status = 'open';
Tes praktis: jalankan query admin yang lambat dengan dan tanpa filter status. Jika tetap lambat, indeks hanya status tidak akan menyelamatkannya. Fokus pada sort dan filter kedua yang benar-benar memperkecil daftar.
Filtering assignee: indeks equality dan kombinasi umum
Di kebanyakan panel admin, assignee adalah user ID yang disimpan pada record: foreign key seperti assignee_id. Itu filter equality klasik, dan sering kali kemenangan cepat dengan indeks sederhana.
Assignee juga muncul bersama filter lain karena mencerminkan cara kerja orang. Seorang lead support mungkin memfilter ke "Assigned to Alex" lalu mempersempit ke "Open" untuk melihat apa yang masih perlu ditangani. Jika tampilan ini lambat, Anda sering butuh lebih dari indeks satu kolom.
Mulai yang baik adalah indeks komposit yang cocok dengan kombinasi filter umum:
(assignee_id, status)untuk "item terbuka saya"(assignee_id, status, updated_at)jika daftar juga diurutkan berdasarkan aktivitas terbaru
Urutan penting dalam indeks komposit. Letakkan filter equality dulu (sering assignee_id, lalu status), dan letakkan kolom sort atau range terakhir (updated_at). Itu selaras dengan apa yang bisa dipakai database secara efisien.
Unassigned adalah jebakan umum. Banyak sistem merepresentasikan "unassigned" sebagai NULL pada assignee_id, dan manajer sering memfilternya. Bergantung pada database dan bentuk query, nilai NULL bisa mengubah rencana sehingga indeks yang bekerja bagus untuk item yang ditugaskan terasa tidak berguna untuk unassigned.
Jika unassigned adalah alur kerja utama, pilih satu pendekatan jelas dan uji:
- Biarkan
assignee_idnullable, tapi pastikanWHERE assignee_id IS NULLdiuji dan diindeks ketika perlu. - Gunakan nilai khusus (seperti user "Unassigned") hanya jika cocok dengan model data Anda.
- Tambahkan partial index untuk baris unassigned jika database Anda mendukungnya.
Jika Anda membangun panel admin di AppMaster, membantu untuk mencatat filter dan sort yang tim gunakan paling sering, lalu mencerminkan pola itu dengan sejumlah kecil indeks yang dipilih dengan baik daripada mengindeks setiap field yang kebetulan tersedia.
Rentang tanggal: indeks yang cocok dengan cara orang memfilter
Filter tanggal biasanya muncul sebagai preset cepat seperti "last 7 days" atau "last 30 days", plus picker kustom dengan tanggal mulai dan akhir. Mereka terlihat sederhana, tapi dapat memicu pekerjaan database yang sangat berbeda pada tabel besar.
Pertama, pastikan kolom timestamp mana yang sebenarnya dimaksud orang. Gunakan:
created_atuntuk tampilan "item baru"updated_atuntuk tampilan "baru saja diubah"
Pasang indeks btree normal pada kolom itu. Tanpanya, setiap klik "last 30 days" bisa berubah menjadi full table scan.
Preset rentang sering terlihat seperti created_at >= now() - interval '30 days'. Itu kondisi range, dan indeks pada created_at bisa dipakai secara efisien. Jika UI juga mengurutkan terbaru dulu, mencocokkan arah sort (misalnya, created_at DESC di PostgreSQL) dapat membantu pada daftar yang sering dipakai.
Ketika rentang tanggal digabungkan dengan filter lain (status, assignee), pilih secara selektif. Indeks komposit bagus ketika kombinasi itu umum. Kalau tidak, mereka menambah biaya tulis tanpa memberi manfaat.
Aturan praktis:
- Jika kebanyakan tampilan memfilter berdasarkan status lalu tanggal,
(status, created_at)bisa membantu. - Jika status opsional tapi tanggal selalu ada, pertahankan indeks
created_atsederhana dan hindari banyak composite. - Jangan buat setiap kombinasi. Setiap indeks baru menambah storage dan memperlambat penulisan.
Timezone dan batas menyebabkan banyak bug "missing records". Jika pengguna memilih tanggal (bukan waktu), putuskan bagaimana mengartikan tanggal akhir. Pola aman adalah start inklusif dan end eksklusif: created_at >= start dan created_at < end_next_day. Simpan timestamp dalam UTC dan konversi input pengguna ke UTC sebelum query.
Contoh: admin ops memilih 10 Jan sampai 12 Jan dan mengharapkan melihat item dari seluruh 12 Jan. Jika query Anda menggunakan <= '2026-01-12 00:00', Anda akan kehilangan hampir semua dari 12 Jan. Indeksnya baik, tapi logika boundary-nya salah.
Field teks: pencarian eksak vs pencarian contains
Pencarian teks adalah tempat banyak panel admin melambat, karena orang mengharapkan satu kotak untuk menemukan segalanya. Perbaikan pertama adalah memisahkan dua kebutuhan berbeda: exact match (cepat dan dapat diprediksi) vs contains search (fleksibel, tapi lebih berat).
Exact match mencakup order ID, nomor tiket, email, telepon, atau referensi eksternal. Ini sempurna untuk indeks database normal. Jika admin sering menempelkan ID atau email, indeks sederhana plus query equality bisa membuatnya terasa instan.
Contains search adalah ketika seseorang mengetik fragmen seperti "refund" atau "john" dan mengharapkan kecocokan pada nama, catatan, dan deskripsi. Ini sering diimplementasikan sebagai LIKE %term%. Wildcard di depan membuat indeks B-tree normal tidak dapat mempersempit pencarian, jadi database memindai banyak baris.
Cara praktis membangun pencarian tanpa membebani database:
- Jadikan pencarian eksak sebagai jalur utama (ID, email, username) dan beri label jelas.
- Untuk pencarian "starts with" (
term%), indeks standar dapat membantu dan sering cukup untuk nama. - Tambahkan pencarian contains hanya jika log atau keluhan menunjukkan perlu.
- Saat menambahkannya, gunakan alat yang tepat (PostgreSQL full-text search atau trigram index) daripada berharap indeks normal memperbaiki
LIKE %term%.
Aturan input lebih penting daripada yang diperkirakan banyak tim. Mereka mengurangi beban dan membuat hasil konsisten:
- Tentukan panjang minimum untuk pencarian contains (misalnya, 3+ karakter).
- Normalisasi huruf besar/kecil atau gunakan perbandingan case-insensitive secara konsisten.
- Pangkas spasi di awal dan akhir dan ringkas spasi berulang.
- Perlakukan email dan ID sebagai eksak secara default, meski dimasukkan ke kotak pencarian umum.
- Jika istilah terlalu umum, minta pengguna memperjelas alih-alih menjalankan query yang besar.
Contoh kecil: manajer support mencari "ann" untuk menemukan pelanggan. Jika sistem Anda menjalankan LIKE %ann% di catatan, nama, dan alamat, itu bisa memindai ribuan record. Jika Anda pertama-tama memeriksa field eksak (email atau customer ID), lalu turun ke indeks teks yang lebih cerdas hanya bila perlu, pencarian tetap cepat tanpa membuat setiap query menjadi latihan berat untuk database.
Alur kerja langkah-demi-langkah untuk menambahkan indeks dengan aman
Indeks mudah ditambahkan dan mudah disesali. Alur aman membuat Anda fokus pada filter yang diandalkan admin, dan membantu menghindari indeks "mungkin berguna" yang memperlambat tulis nanti.
Mulai dengan penggunaan nyata. Ambil top query dengan dua cara:
- query yang paling sering
- query yang paling lambat
Untuk panel admin, ini biasanya halaman daftar dengan filter dan pengurutan.
Selanjutnya, tangkap bentuk query persis seperti yang dilihat database. Tuliskan WHERE dan ORDER BY secara tepat, termasuk arah sort dan kombinasi umum (misalnya: status = 'open' AND assignee_id = 42 ORDER BY created_at DESC). Perbedaan kecil dapat mengubah indeks mana yang membantu.
Gunakan loop sederhana:
- Pilih satu query lambat dan satu perubahan indeks untuk dicoba.
- Tambah atau sesuaikan satu indeks.
- Ukur ulang dengan filter dan sort yang sama.
- Periksa bahwa insert dan update tidak menjadi jelas lebih lambat.
- Pertahankan perubahan hanya jika jelas meningkatkan query target.
Pagination perlu pemeriksaan sendiri. Pagination berbasis offset (OFFSET 20000) sering melambat saat melangkah jauh, bahkan dengan indeks. Jika pengguna rutin melompat ke halaman sangat dalam, pertimbangkan pagination gaya cursor ("tunjukkan item sebelum timestamp/id ini") sehingga indeks bisa melakukan pekerjaan yang konsisten pada tabel besar.
Akhirnya, simpan catatan kecil agar daftar indeks Anda tetap dapat dipahami beberapa bulan kemudian: nama indeks, tabel, kolom (dan urutan), serta query yang didukung.
Kesalahan umum pengindeksan di panel admin
Cara tercepat membuat panel admin terasa lambat adalah menambahkan indeks tanpa memeriksa bagaimana orang sebenarnya memfilter, mengurutkan, dan melakukan pagination. Indeks menghabiskan ruang dan menambah kerja pada setiap insert dan update.
Kesalahan yang paling sering muncul
Pola ini menyebabkan sebagian besar masalah:
- Mengindeks setiap kolom "sekedar kalau-kalau".
- Membuat indeks komposit dengan urutan kolom yang salah.
- Mengabaikan pengurutan dan pagination.
- Mengira indeks normal bisa memperbaiki contains search seperti
LIKE '%term%'. - Meninggalkan indeks lama setelah perubahan UI.
Skenario umum: tim support memfilter tiket dengan Status = Open, mengurutkan berdasarkan waktu update, dan mempaginasikan hasil. Jika Anda hanya menambahkan indeks pada status, database mungkin masih harus mengumpulkan semua tiket open dan mengurutkannya. Indeks yang mencocokkan filter dan sort bersama bisa mengembalikan halaman 1 dengan cepat.
Cara cepat menangkap masalah ini
Sebelum dan sesudah perubahan UI admin, lakukan review singkat:
- Daftar filter teratas dan sort default, lalu konfirmasi ada indeks yang cocok dengan pola
WHERE + ORDER BY. - Periksa wildcard di depan (
LIKE '%term%') dan putuskan apakah pencarian contains benar-benar diperlukan. - Cari indeks duplikat atau tumpang tindih.
- Lacak indeks yang tidak terpakai untuk beberapa waktu, lalu hapus ketika yakin tidak dibutuhkan.
Jika Anda membangun panel admin di AppMaster pada PostgreSQL, jadikan review ini bagian dari pengiriman layar baru. Indeks yang tepat cenderung mengikuti secara langsung dari filter dan urutan yang UI Anda gunakan.
Pemeriksaan cepat dan langkah selanjutnya
Sebelum menambahkan lebih banyak indeks, pastikan indeks yang sudah ada membantu filter yang benar-benar dipakai setiap hari. Panel admin yang baik terasa instan pada jalur umum, bukan pada pencarian jarang.
Beberapa pemeriksaan menangkap sebagian besar masalah:
- Buka kombinasi filter yang paling umum (status, assignee, rentang tanggal, plus sort default) dan pastikan tetap cepat saat tabel tumbuh.
- Untuk setiap tampilan lambat, verifikasi query memakai indeks yang cocok dengan
WHEREdanORDER BY, bukan hanya salah satu bagian. - Jaga daftar indeks cukup kecil sehingga Anda bisa menjelaskan tujuan tiap indeks dalam satu kalimat.
- Awasi aksi yang banyak menulis (create, update, status change). Jika itu menjadi lebih lambat setelah pengindeksan, mungkin Anda memiliki terlalu banyak atau indeks yang tumpang tindih.
- Tentukan apa arti "search" di UI Anda: exact match, prefix, atau contains. Rencana pengindeksan harus sesuai pilihan itu.
Langkah praktis selanjutnya adalah menuliskan jalur emas Anda sebagai kalimat biasa, seperti: "Support agents memfilter tiket terbuka, ditugaskan ke saya, 7 hari terakhir, diurutkan berdasarkan terbaru." Gunakan kalimat-kalimat itu untuk merancang sejumlah kecil indeks yang jelas mendukungnya.
Jika Anda masih tahap awal pembangunan, ada baiknya memodelkan data dan filter default sebelum membuat terlalu banyak layar. Dengan AppMaster (appmaster.io), Anda bisa iterasi tampilan admin dengan cepat, lalu menambahkan beberapa indeks yang cocok dengan apa yang tim Anda benar-benar pakai setelah penggunaan nyata memperjelas jalur panas.
FAQ
Mulailah dengan query yang berjalan terus-menerus: tampilan daftar default yang pertama dilihat admin, plus 2–3 filter yang paling sering diklik sepanjang hari. Ukur frekuensi dan rasa sakit (yang paling lambat dan paling sering digunakan), lalu indeks hanya yang jelas mengurangi waktu tunggu pada bentuk query tersebut.
Karena filter yang berbeda memaksa jumlah kerja yang berbeda. Beberapa filter mempersempit ke sejumlah kecil baris, sementara yang lain menyentuh jangkauan besar atau memerlukan pengurutan pada set hasil yang besar, sehingga satu query dapat memanfaatkan indeks dengan baik sementara yang lain tetap melakukan scan dan sort pada banyak data.
Tidak selalu. Jika sebagian besar baris memiliki status yang sama, indeks pada status saja sering kali tidak banyak membantu. Indeks berguna lebih ketika status itu jarang, atau saat Anda mencocokkan tampilan nyata dengan mengindeks status bersama dengan kolom sort atau filter lain yang benar-benar memperkecil hasil.
Gunakan indeks komposit yang mencerminkan apa yang orang lakukan, misalnya memfilter berdasarkan status dan mengurutkan berdasarkan aktivitas terbaru. Di PostgreSQL, partial index bisa jadi solusi bersih ketika satu status mendominasi, karena mempertahankan indeks yang lebih kecil dan fokus pada alur kerja yang umum.
Indeks sederhana pada assignee_id sering kali cepat karena ini filter equality. Jika “my open items” adalah workflow inti, indeks komposit yang dimulai dengan assignee_id lalu status (dan opsional kolom sort) biasanya lebih baik daripada indeks kolom tunggal terpisah.
Unassigned sering disimpan sebagai NULL, dan WHERE assignee_id IS NULL dapat berperilaku berbeda dari WHERE assignee_id = 123. Jika antrean unassigned penting, uji query itu secara spesifik dan tambahkan strategi indeks yang mendukungnya, sering kali partial index yang menargetkan baris unassigned jika database Anda mendukungnya.
Tambahkan indeks btree pada kolom timestamp yang memang difilter orang, biasanya created_at untuk tampilan “new items” dan updated_at untuk tampilan “recently changed.” Jika Anda juga mengurutkan berdasarkan yang terbaru, indeks yang mencocokkan arah sort dapat membantu, tapi batasi composite ke beberapa kombinasi yang benar-benar sering digunakan.
Sebagian besar bug missing records muncul dari batas tanggal, bukan indeks. Pola yang andal adalah termasuk start dan eksklusif end, mengonversi tanggal yang dipilih pengguna ke UTC dan query >= start dan < end_next_day, sehingga Anda tidak secara tidak sengaja menghilangkan semua aktivitas pada hari akhir.
Karena query “contains” seperti LIKE %term% tidak bisa memanfaatkan indeks btree biasa untuk langsung lompat ke kecocokan, sehingga melakukan banyak scan. Perlakukan lookup eksak (ID, email, nomor order) sebagai jalur cepat utama, dan hanya tambahkan pencarian contains bila perlu menggunakan metode yang tepat seperti full-text atau trigram.
Menambahkan terlalu banyak indeks menambah penggunaan storage dan memperlambat insert serta update, dan Anda tetap bisa melewatkan bottleneck sebenarnya jika indeks tidak cocok dengan pola WHERE + ORDER BY. Loop yang lebih aman: ubah satu indeks pada satu waktu, ukur ulang query lambat yang sama, dan simpan hanya perubahan yang jelas meningkatkan hot path.
Jika Anda membangun layar admin di AppMaster, catat filter dan sort yang tim Anda gunakan paling sering, lalu tambahkan set indeks kecil yang mencerminkan tampilan nyata tersebut daripada mengindeks setiap field yang tersedia.


