Berkas CSV yang diimpor jarang datang bersih, dan Neo4j tidak menolak data cacat dengan sendirinya. Topik ini membahas tiga lapis pengamanan pada pipeline impor, yaitu rangkaian tahap berkas mentah hingga graf siap pakai. Lapis pertama adalah constraint keunikan yang mendukung MERGE, lapis kedua penanganan baris duplikat, dan lapis ketiga pembersihan nilai kosong, spasi berlebih, serta tipe yang salah dengan trim, toInteger, toFloat, coalesce, dan CASE. Seluruh contoh dijalankan pada server Neo4j 2026.10 dengan berkas pegawai-kotor.csv yang sengaja kotor. Setelah topik ini dibaca, impor dapat dijalankan ulang tanpa menciptakan node kembar dan hasilnya dapat diperiksa dengan kueri validasi yang tetap.
Constraint keunikan pendukung MERGE#
MERGE bekerja dengan pola get-or-create, yaitu mencari dahulu, lalu membentuk node baru apabila pencariannya kosong. Constraint keunikan pada properti kunci memberikan dua jaminan sekaligus bagi proses impor. Basis data menolak penulisan yang menghasilkan dua node dengan nilai kunci sama, sehingga kekeliruan kueri tidak berubah menjadi data kembar yang masuk tanpa pesan. Constraint juga membawa index, yaitu struktur data yang mempercepat pencarian kunci pada setiap baris berkas.
Constraint dibuat terlebih dahulu, sebelum berkas mana pun diimpor. Kueri berikut membuat constraint keunikan untuk properti nama pada label :Karyawan, lalu menampilkannya dengan filter nama agar keluarannya stabil. Bentuk IF NOT EXISTS membuat pernyataan ini idempoten, yaitu aman dijalankan ulang. Penanda _m5 pada namanya menandai aset milik modul ini.
CREATE CONSTRAINT karyawan_nama_m5 IF NOT EXISTS
FOR (k:Karyawan) REQUIRE k.nama IS UNIQUE;
SHOW CONSTRAINTS YIELD name, type, labelsOrTypes, properties
WHERE name = "karyawan_nama_m5"
RETURN name, type, labelsOrTypes, properties;name, type, labelsOrTypes, properties
"karyawan_nama_m5", "NODE_PROPERTY_UNIQUENESS", ["Karyawan"], ["nama"]Keluaran menegaskan tipe NODE_PROPERTY_UNIQUENESS yang kini mengawasi properti nama, sehingga setiap MERGE dengan kunci nama berjalan di atas jaminan keunikan yang ditegakkan basis data, bukan sekadar konvensi penulisan kueri.
Berkas contoh dengan cacat data#
Berkas pegawai-kotor.csv mewakili hasil ekspor dari sistem lain yang belum melalui pemeriksaan apa pun. Berkas ini memuat satu baris header dan enam baris data dengan empat cacat yang sengaja ditanam.
- Baris Eri muncul dua kali dengan isi yang sama persis, mewakili duplikasi baris pada sumber data.
- Baris Fajar memuat spasi pada kedua sisi nama, yaitu kolom yang menjadi kunci
MERGE, dan kolomtahunMasukkosong. - Baris Gilang menuliskan tahun sebagai teks “dua ribu” yang bukan angka.
- Baris Hana memuat spasi di awal nilai
tahunMasuk.
nama,divisi,tahunMasuk
Agus,Infra,2019
Eri,Infra,2020
Eri,Infra,2020
Fajar ,Data,
Gilang,Produk,dua ribu
Hana,Data, 2023Setiap kelas cacat menuntut penanganan yang berbeda pada bagian-bagian berikutnya. Kolom tahunMasuk digunakan sebagai contoh nilai yang harus menjadi bilangan, sedangkan kolom nama dan divisi mewakili teks yang mungkin mengandung spasi. Isi berkas diperiksa melalui terminal sebelum digunakan, dan salinannya wajib berada pada direktori import server sebelum dibaca LOAD CSV. Kueri impor selanjutnya membaca salinan itu melalui URL file:///pegawai-kotor.csv.
# menampilkan isi berkas contoh dari direktori kerja
head pegawai-kotor.csvnama,divisi,tahunMasuk
Agus,Infra,2019
Eri,Infra,2020
Eri,Infra,2020
Fajar ,Data,
Gilang,Produk,dua ribu
Hana,Data, 2023# tidak-dijalankan
# menyalin berkas contoh ke direktori import server Neo4j modul ini
cp pegawai-kotor.csv /home/kal/my-project/neo4j/inst-m1/import/Deduplikasi baris duplikat dengan MERGE#
Setiap baris diubah menjadi node :Karyawan dengan nama sebagai kunci pencocokan, sedangkan divisi dan tahun masuk dituliskan melalui SET. Fungsi trim dipanggil pada kolom nama sejak kueri pertama, karena spasi yang tersimpan pada kunci membuat penulisan yang tampak sama menjadi dua kunci yang berbeda, sehingga MERGE membentuk node kembar. Kolom tahun masuk melewati toInteger karena LOAD CSV menyimpan nilai CSV sebagai string.
LOAD CSV WITH HEADERS FROM 'file:///pegawai-kotor.csv' AS baris
MERGE (k:Karyawan {nama: trim(baris.nama)})
SET k.divisi = baris.divisi,
k.tahunMasuk = toInteger(baris.tahunMasuk);
MATCH (k:Karyawan)
RETURN count(k) AS jumlahKaryawan;jumlahKaryawan
8Angka delapan berasal dari lima node :Karyawan lama pada graf modul ini ditambah tiga node baru. Enam baris data berubah menjadi tiga node karena baris Eri yang ganda menemukan node yang sama pada kedua kemunculannya. Kueri yang sama dijalankan ulang dan keluarannya tetap delapan, menandakan pola ini idempoten. Daftar nama berikut memperlihatkan keadaan tiap node.
MATCH (k:Karyawan)
WHERE k.nama IN ['Agus', 'Eri', 'Fajar', 'Gilang', 'Hana']
RETURN k.nama AS nama, k.divisi AS divisi, k.tahunMasuk AS tahunMasuk
ORDER BY nama;nama, divisi, tahunMasuk
"Agus", "Infra", 2019
"Eri", "Infra", 2020
"Fajar", "Data", NULL
"Gilang", "Produk", NULL
"Hana", "Data", 2023Keluaran memperlihatkan dua kekosongan tanpa pesan error apa pun. Fajar dan Gilang kehilangan properti tahunMasuk karena toInteger mengembalikan null untuk string kosong maupun teks yang bukan angka, sedangkan SET dengan null menghapus properti dari node.
Nilai kosong dan nilai default defensif#
Nilai kosong dan kegagalan konversi diberi perlakuan tetap, yaitu ditandai dengan nilai default yang disepakati alih-alih dibiarkan menghilang. Pada kueri berikut, CASE memeriksa kolom tahunMasuk setelah dibersihkan dengan trim. Kolom yang kosong dituliskan sebagai 0, sedangkan kolom berisi teks lain melewati toInteger yang dijaga coalesce. Apabila konversi gagal dan menghasilkan null, coalesce menggantinya dengan 0. Nilai 0 bukan tahun yang sah, melainkan penanda nilai yang belum valid, sehingga mudah dicari saat audit.
LOAD CSV WITH HEADERS FROM 'file:///pegawai-kotor.csv' AS baris
MERGE (k:Karyawan {nama: trim(baris.nama)})
SET k.divisi = trim(baris.divisi),
k.tahunMasuk = CASE
WHEN trim(baris.tahunMasuk) = '' THEN 0
ELSE coalesce(toInteger(baris.tahunMasuk), 0)
END;
MATCH (k:Karyawan)
WHERE k.nama IN ['Fajar', 'Gilang', 'Hana']
RETURN k.nama AS nama, k.tahunMasuk AS tahunMasuk
ORDER BY nama;nama, tahunMasuk
"Fajar", 0
"Gilang", 0
"Hana", 2023Fajar menyimpan penanda 0 karena kolomnya kosong, Gilang menyimpan penanda yang sama karena teksnya gagal dikonversi, dan Hana menyimpan 2023 karena spasi di awal nilai tidak mengganggu konversi. Impor ulang ini aman dijalankan karena MERGE menemukan node yang sudah ada, sehingga penulisan ulang properti terjadi pada node yang sama. trim pada kolom divisi ikut diterapkan sebagai kebiasaan defensif. Penanda lain dapat digunakan asalkan nilainya bukan nilai data yang sah.
Memperbaiki tipe yang salah saat impor#
Berkas CSV tidak menyimpan tipe data, sehingga keputusan tipe seluruhnya berada pada kueri impor. Fungsi toInteger mengubah string menjadi bilangan bulat, sedangkan toFloat menangani nilai desimal semacam berat atau skor. Keduanya mengembalikan null ketika konversi tidak mungkin dilakukan, bukan error. Perilakunya dapat diperiksa dengan daftar nilai uji melalui UNWIND.
UNWIND ['2023', ' 2023', '', 'dua ribu'] AS s
RETURN s AS teks, toInteger(s) AS hasil
ORDER BY teks;teks, hasil
"", NULL
" 2023", 2023
"2023", 2023
"dua ribu", NULLSpasi di awal maupun akhir nilai ditoleransi, sehingga " 2023" tetap menjadi 2023, sedangkan string kosong dan teks yang bukan angka menghasilkan null. Karena spasi ditoleransi oleh konversi angka, trim tetap diperlukan bukan untuk toInteger, melainkan untuk kunci pencocokan dan perbandingan teks. toFloat berperilaku sama pada ranah desimalnya.
UNWIND ['4.75', '4,75', 'empat'] AS s
RETURN s AS teks, toFloat(s) AS hasil
ORDER BY teks;teks, hasil
"4,75", NULL
"4.75", 4.75
"empat", NULLNilai dengan tanda koma sebagai pemisah desimal tidak diterima, sebagaimana terlihat pada “4,75” yang menghasilkan null. Apabila sumber data menggunakan koma, koma diganti dengan titik melalui replace sebelum toFloat dipanggil. Penjaganya tetap sama, yaitu coalesce yang memberi nilai default apabila hasil konversinya null.
Validasi pascaimpor#
Impor yang selesai belum berarti benar, sehingga hasilnya diperiksa dengan kueri yang tetap. Pemeriksaan pertama menghitung jumlah node dan jumlah relasi pada label modul ini. Angka pembandingnya dinyatakan lebih dahulu, karena selisih yang tidak terjelaskan menandakan impor yang berjalan sebagian atau berulang.
MATCH (k:Karyawan)
RETURN count(k) AS jumlahKaryawan;jumlahKaryawan
8MATCH (:Karyawan)-[r:ANGGOTA_DARI]->(:Divisi)
RETURN count(r) AS jumlahRelasi;jumlahRelasi
5Node dengan penanda 0 atau properti yang hilang dilaporkan namanya, sehingga baris bermasalah dapat ditelusuri kembali.
MATCH (k:Karyawan)
WHERE k.tahunMasuk = 0 OR k.tahunMasuk IS NULL
RETURN k.nama AS nama, k.tahunMasuk AS tahunMasuk
ORDER BY nama;nama, tahunMasuk
"Fajar", 0
"Gilang", 0Pemeriksaan ketiga mencari node tanpa relasi, yaitu node yang berdiri sendiri karena tahap pembentukan relasinya belum berjalan atau gagal.
MATCH (k:Karyawan)
WHERE NOT (k)--()
RETURN k.nama AS nama
ORDER BY nama;nama
"Fajar"
"Gilang"
"Hana"Keluaran terakhir menemukan ketiga node baru tanpa relasi. Temuan ini sesuai keadaannya karena impor berkas kotor hanya membentuk node, sedangkan relasi :ANGGOTA_DARI perlu dibentuk pada tahap tersendiri. Validasi menandai pekerjaan yang belum selesai sebelum data digunakan lebih lanjut.
Kesalahan umum#
Empat kesalahan berikut paling sering muncul saat topik ini dipraktikkan.
- Spasi pada kolom kunci tidak dibersihkan. Dua penulisan nama yang tampak sama menjadi dua kunci yang berbeda, sehingga
MERGEmembentuk node kembar tanpa pesan error. Perbaikannya adalah memanggiltrimpada kolom kunci dan menghapus node kembar yang sudah terbentuk. - Konversi yang gagal dianggap akan menghentikan impor dengan error.
toIntegerdantoFloatjustru mengembalikan null, danSETdengan null menghapus propertinya dalam senyap. Perbaikannya adalah membungkus konversi dengancoalescedan menandai hasilnya dengan nilai default. - Nilai desimal dengan pemisah koma diberikan langsung kepada
toFloat. Nilai “4,75” menghasilkan null karena pemisah desimal yang diterima adalah titik. Perbaikannya adalah mengganti koma dengan titik melaluireplacesebelum konversi. - Constraint keunikan dibuat setelah data duplikat masuk. Pembuatan constraint ditolak basis data karena data yang sudah ada melanggar aturan yang hendak ditegakkan. Perbaikannya adalah menghapus duplikat terlebih dahulu, kemudian membuat constraint tersebut.
Ringkasan#
- Constraint keunikan pada properti kunci dibuat sebelum impor melalui
CREATE CONSTRAINT IF NOT EXISTS, sehinggaMERGEmendapatkan pencarian kunci yang cepat dan keunikan yang ditegakkan. MERGEmencegah node kembar pada baris duplikat, sedangkantrimmenjaga dua penulisan yang tampak sama tetap terbaca sebagai satu kunci.- Nilai kosong dan konversi yang gagal ditangani dengan
CASEdancoalescemenuju nilai default penanda, sedangkantoIntegersertatoFloatmengubah string menjadi angka atau null saat konversi mustahil. - Validasi pascaimpor menghitung node dan relasi per label, mencari nilai penanda beserta properti yang hilang, dan melaporkan node tanpa relasi.
Referensi#
- MERGE — Neo4j Docs
— Sintaks
MERGE, perilaku get-or-create, serta klausaON CREATEdanON MATCH. - Constraints — Neo4j Docs
— Sintaks constraint keunikan pada properti node beserta perilaku
IF NOT EXISTS. - Scalar functions — Neo4j Docs
— Dokumentasi
coalesce,toInteger, dantoFloattermasuk perilaku null pada konversi yang gagal.