Pertanyaan paling sering di tim data TokoKita ternyata bukan soal algoritma: “tabel yang berisi email pelanggan itu yang mana?” Tidak ada yang bisa menjawab pasti, karena ratusan tabel tersebar di warehouse dan data lake tanpa inventaris. Akibatnya analisis dibangun ulang di atas tabel yang salah, data pribadi ikut tersalin ke tempat yang tidak seharusnya, dan engineer baru butuh berbulan-bulan untuk memahami peta data. Data catalog menutup masalah itu: satu tempat yang mendata aset data beserta metadatanya — schema, owner, tags, klasifikasi — lengkap dengan pencarian. Pada bab ini kita membangun catalog mini dari nol dengan Python, lalu membandingkannya dengan platform catalog yang lazim dipakai industri.
Tiga Jenis Metadata #
Katalog yang hanya berisi nama tabel dan daftar kolom hanya menjawab separuh pertanyaan. Pertanyaan bisnis seperti “kolom total di laporan keuangan dihitung dengan definisi yang mana?” membutuhkan konteks yang tidak ada di dalam berkas data itu sendiri. Catalog yang lengkap menyimpan tiga jenis metadata sekaligus:
| Jenis metadata | Isi | Contoh untuk pelanggan.csv |
|---|---|---|
| Teknis | schema, format, jumlah baris, lokasi | email:string, 2 baris, zona landing |
| Operasional | owner, steward, pipeline penghasil, jadwal refresh | dimuat harian oleh pipeline ingestion |
| Bisnis | definisi kolom, tags, glossary, klasifikasi | total = harga kali qty setelah diskon |
Metadata teknis bisa dipanen otomatis dari berkas data itu sendiri — inilah yang membuat catalog tetap segar tanpa pengetikan manual. Metadata bisnis dan operasional harus diisi manusia melalui berkas konfigurasi, dan di TokoKita kita simpan sebagai pemetaan YAML yang di-review seperti kode.
Membangun Mini Catalog #
Langkah pertama adalah peta pemilik: berkas YAML yang memetakan nama aset ke owner, steward, tags, dan klasifikasi. Tags inilah yang nanti menghubungkan katalog dengan pembahasan Chapter Klasifikasi Data — aset bertag pii langsung teridentifikasi sensitif tanpa membuka isinya:
# Pemetaan aset TokoKita ke owner, steward, tags, dan klasifikasi.
pemilik:
pesanan.parquet:
owner: tim-transaksi
steward: Dewi
tags: [transaksi, finansial]
klasifikasi: internal
pelanggan.csv:
owner: tim-crm
steward: Budi
tags: [pii, pelanggan]
klasifikasi: restrictedSkrip berikut memindai direktori data, mengekstrak schema dan jumlah baris dari Parquet (lewat pyarrow) maupun CSV, menggabungkannya dengan pemetaan di atas, lalu mencetak katalog dan menjalankan dua pencarian:
"""Mini data catalog: pindai berkas data, ekstrak schema, gabungkan owner dari YAML."""
import csv
import shutil
from pathlib import Path
import pyarrow as pa
import pyarrow.parquet as pq
import yaml
DASAR = Path(__file__).parent / "data_catalog"
PEMILIK = Path(__file__).parent / "owners.yaml"
def siapkan_data():
"""Bangun ulang berkas contoh agar demo selalu bisa diulang."""
if DASAR.exists():
shutil.rmtree(DASAR)
DASAR.mkdir(parents=True)
tabel = pa.table({
"pesanan_id": [1001, 1002, 1003],
"pelanggan_id": [7, 8, 7],
"total": [460000, 125000, 310000],
})
pq.write_table(tabel, DASAR / "pesanan.parquet")
(DASAR / "pelanggan.csv").write_text(
"pelanggan_id,nama,email,kota\n"
"7,Budi,[email protected],Jakarta\n"
"8,Siti,[email protected],Bandung\n"
)
def baca_metadata(berkas):
"""Ekstrak daftar kolom dan jumlah baris dari parquet atau csv."""
if berkas.suffix == ".parquet":
skema = pq.read_schema(berkas)
baris = pq.read_metadata(berkas).num_rows
kolom = [f"{field.name}:{field.type}" for field in skema]
else:
with berkas.open() as handle:
pembaca = csv.reader(handle)
kolom = [f"{nama}:string" for nama in next(pembaca)]
baris = sum(1 for _ in pembaca)
return kolom, baris
def bangun_katalog():
"""Pindai direktori, gabungkan metadata teknis dengan owner dan tags."""
pemilik = yaml.safe_load(PEMILIK.read_text())["pemilik"]
katalog = []
for berkas in sorted(DASAR.iterdir()):
kolom, baris = baca_metadata(berkas)
meta = pemilik.get(berkas.name, {})
katalog.append({
"aset": berkas.name,
"format": berkas.suffix.lstrip("."),
"baris": baris,
"kolom": kolom,
"owner": meta.get("owner", "-"),
"steward": meta.get("steward", "-"),
"tags": meta.get("tags", []),
"klasifikasi": meta.get("klasifikasi", "-"),
})
return katalog
def tampilkan(katalog):
print(f"=== Katalog: {len(katalog)} aset ===")
print(f"{'Aset':<18}{'Format':<8}{'Baris':<6}{'Owner':<14}{'Klasifikasi':<12}Tags")
for entri in katalog:
tag = ", ".join(entri["tags"])
print(f"{entri['aset']:<18}{entri['format']:<8}{entri['baris']:<6}"
f"{entri['owner']:<14}{entri['klasifikasi']:<12}{tag}")
daftar_kolom = ", ".join(entri["kolom"])
print(f"{'':<18}kolom: {daftar_kolom}")
def cari(katalog, kunci, nilai):
"""Cari aset berdasarkan tag atau nama kolom."""
if kunci == "tag":
return [e["aset"] for e in katalog if nilai in e["tags"]]
return [e["aset"] for e in katalog if any(nilai in kolom for kolom in e["kolom"])]
def jalankan():
siapkan_data()
katalog = bangun_katalog()
tampilkan(katalog)
print("=== Pencarian ===")
print("Tag 'pii' :", cari(katalog, "tag", "pii"))
print("Kolom mengandung 'email' :", cari(katalog, "kolom", "email"))
if __name__ == "__main__":
jalankan()Output yang Dihasilkan:
=== Katalog: 2 aset ===
Aset Format Baris Owner Klasifikasi Tags
pelanggan.csv csv 2 tim-crm restricted pii, pelanggan
kolom: pelanggan_id:string, nama:string, email:string, kota:string
pesanan.parquet parquet 3 tim-transaksi internal transaksi, finansial
kolom: pesanan_id:int64, pelanggan_id:int64, total:int64
=== Pencarian ===
Tag 'pii' : ['pelanggan.csv']
Kolom mengandung 'email' : ['pelanggan.csv']Mari kita telaah baris-baris kode di atas:
- Baris 14-29 (
siapkan_data): membuat dua berkas contoh — satu Parquet tiga kolom, satu CSV berisi data pelanggan — sehingga demo bisa dijalankan ulang kapan pun. - Baris 32-43 (
baca_metadata): cabang Parquet membaca schema dan metadata baris lewat pyarrow tanpa memuat data; cabang CSV membaca header lalu menghitung baris. Tipe kolom CSV seragamstringkarena CSV memang tidak menyimpan tipe. - Baris 46-63 (
bangun_katalog): untuk tiap berkas, metadata teknis digabung dengan entri YAML lewatpemilik.get(berkas.name, {})— aset yang belum terpetakan tetap masuk katalog dengan owner-, sehingga kekosongan terlihat alih-alih tersembunyi. - Baris 77-81 (
cari): dua mode pencarian. Modetagmencocokkan keanggotaan tag; modekolommencocokkan substring pada nama kolom, sehingga pencarianemailmenemukan aset mana pun yang memiliki kolom bernama demikian.
Perhatikan hasil pencarian di akhir output: pertanyaan pembuka bab ini — “tabel mana yang berisi email pelanggan?” — kini terjawab satu baris. Pencarian tag pii juga memberi daftar aset yang wajib mendapat kontrol paling ketat sesuai klasifikasinya, dari masking sampai retention yang pendek. Mode pencarian kolom menjadi bekal analisis dampak: sebelum sebuah kolom diubah atau dihapus dari tabel sumber, kita bisa mencari semua aset lain yang kolomnya bernama sama dan mengecek apakah ada yang terdampak.
Apache Atlas, DataHub, dan OpenMetadata #
Catalog mini kita bekerja untuk puluhan aset, tetapi platform data yang lebih besar butuh catalog dengan antarmuka pencarian, integrasi otomatis ke warehouse dan pipeline, serta API. Tiga platform open source yang paling sering dipakai:
| Aspek | Apache Atlas | DataHub | OpenMetadata |
|---|---|---|---|
| Asal | proyek Apache, ekosistem Hadoop | dikembangkan di LinkedIn, kini open source | proyek open source dengan standar metadata JSON terbuka |
| Kekuatan | integrasi Hive/Hadoop dan Apache Ranger untuk authorization | integrasi luas ke warehouse, streaming, dan BI | kolaborasi dan data quality menyatu di satu tempat |
| Lineage | capture dari Hive query | otomatis dari integrasi pipeline | mendukung standar OpenLineage |
| Dokumentasi | atlas.apache.org | datahubproject.io | open-metadata.org |
Ketiganya menyediakan apa yang kita bangun manual di atas — pemindaian schema, metadata owner, tag, pencarian — ditambah antarmuka web dan sinkronisasi otomatis. Pemilihannya biasanya mengikuti ekosistem yang sudah dipakai: Atlas kuat di klaster Hadoop dengan Ranger, DataHub menonjol pada jumlah konektor, dan OpenMetadata menggabungkan catalog dengan pengelolaan kualitas.
Katalog menjawab “aset apa saja yang ada dan apa isinya”, tetapi belum menjawab “dari mana aset ini berasal dan bagaimana aset ini dihasilkan” — pertanyaan yang digarap oleh Chapter Data Lineage dan Provenance.