LOAD CSV membaca baris demi baris dari berkas CSV dan mengalirkannya ke dalam kueri Cypher, sehingga berkas datar dapat diubah menjadi node dan relasi. Topik ini membahas URL file:/// yang diselesaikan terhadap direktori import server, pembacaan baris header (baris nama kolom) dengan WITH HEADERS, pemisah kolom alternatif melalui FIELDTERMINATOR, pola baris-ke-node dengan MERGE, serta subquery ter-batch CALL { … } IN TRANSACTIONS untuk muatan besar. Seluruh contoh dijalankan pada server Neo4j 2026.10 dengan dataset pegawai dan divisi modul ini. Setelah topik ini dibaca, berkas CSV kecil dapat diimpor menjadi graf secara berulang tanpa duplikat.
Berkas contoh#
Dua berkas CSV digunakan di seluruh contoh pada topik ini. Berkas pegawai.csv memuat satu baris header dan lima baris data pegawai, sedangkan divisi.csv memuat kode singkat dan nama lengkap untuk tiga divisi. Kolom divisi pada pegawai.csv menyimpan nama divisi yang lengkap, sehingga nilainya dapat dicocokkan langsung dengan kolom nama pada divisi.csv. Baris header menyediakan nama kunci untuk setiap kolom, dan kunci itulah yang digunakan kueri untuk mengambil nilai pada setiap baris.
Kedua berkas berikut dibuat pada direktori kerja. Seluruh nilainya berupa teks biasa, termasuk angka tahun pada kolom tahunMasuk, karena format CSV tidak menyimpan tipe data. Nama pegawai berfungsi sebagai kunci alami, yaitu nilai yang membedakan satu pegawai dari pegawai lain, sehingga kunci inilah yang nantinya dituliskan pada pola MERGE. Kelima pegawai terdistribusi ke ketiga divisi, sehingga setiap divisi memuat paling sedikit satu relasi anggota.
nama,divisi,tahunMasuk
Agus,Infra,2019
Bella,Data,2021
Candra,Produk,2018
Dinda,Data,2022
Eri,Infra,2020kode,nama
DT,Data
IF,Infra
PR,ProdukIsi kedua berkas pada direktori kerja dapat diperiksa dengan perintah head sebelum salinannya dikirim ke server.
# menampilkan isi kedua berkas contoh dari terminal
head pegawai.csv divisi.csv==> pegawai.csv <==
nama,divisi,tahunMasuk
Agus,Infra,2019
Bella,Data,2021
Candra,Produk,2018
Dinda,Data,2022
Eri,Infra,2020
==> divisi.csv <==
kode,nama
DT,Data
IF,Infra
PR,ProdukDirektori import dan URL file:///#
URL file:/// pada LOAD CSV diselesaikan terhadap direktori import server Neo4j, bukan terhadap direktori kerja terminal. Pada instalasi baku, direktori tersebut adalah folder import di bawah $NEO4J_HOME, sedangkan pengaturan server.directories.import dapat mengubah lokasinya. Mekanisme ini membatasi akses berkas oleh kueri pada satu direktori, sehingga berkas lain pada server tidak terbaca oleh LOAD CSV. Nama berkas setelah file:/// bersifat relatif, sehingga file:///pegawai.csv menunjuk pegawai.csv yang berada langsung di dalam direktori tersebut.
Perintah berikut menyalin seluruh berkas CSV contoh ke direktori import.
# tidak-dijalankan
# menyalin berkas CSV ke direktori import milik instalasi Neo4j
cp pegawai.csv divisi.csv divisi-titik-koma.csv $NEO4J_HOME/import/Sebelum pola penulisannya disusun, isi berkas diperiksa terlebih dahulu dengan kueri baca yang hanya mengembalikan nilai kolomnya.
LOAD CSV WITH HEADERS FROM 'file:///pegawai.csv' AS baris
RETURN baris.nama AS nama, baris.divisi AS divisi, baris.tahunMasuk AS tahunMasuk
ORDER BY baris.nama;nama, divisi, tahunMasuk
"Agus", "Infra", "2019"
"Bella", "Data", "2021"
"Candra", "Produk", "2018"
"Dinda", "Data", "2022"
"Eri", "Infra", "2020"Keluaran menunjukkan dua hal yang menentukan bentuk kueri berikutnya. Setiap baris terbaca lengkap sesuai header karena WITH HEADERS menjadikan setiap baris sebuah map, yaitu struktur data berkunci nama kolom. Seluruh nilai termasuk tahunMasuk datang sebagai string yang ditandai kutip ganda, karena LOAD CSV menganggap seluruh isi CSV sebagai teks. Konversi tipe dengan toInteger() karenanya dilakukan saat penulisan data ke node.
Pola baris-ke-node dengan MERGE#
Impor graf dari berkas CSV menempuh pola baris-ke-node, yaitu setiap baris data diubah menjadi satu node melalui MERGE yang mencari dahulu sebelum membuat. Impor dijalankan bertahap, dimulai dari node divisi sebagai target relasi pegawai. Kueri berikut mengimpor divisi.csv beserta verifikasinya.
LOAD CSV WITH HEADERS FROM 'file:///divisi.csv' AS baris
MERGE (d:Divisi {kode: baris.kode})
SET d.nama = baris.nama;
MATCH (d:Divisi)
RETURN d.kode AS kode, d.nama AS nama
ORDER BY d.kode;kode, nama
"DT", "Data"
"IF", "Infra"
"PR", "Produk"Properti kode pada pola MERGE berperan sebagai kunci pencocokan, sedangkan nama lengkap divisi diberikan melalui SET agar tidak ikut menentukan pencocokan. Pola ini idempoten, artinya keadaan akhir sama pada setiap eksekusi, karena setiap baris menemukan node hasil eksekusi sebelumnya. Berkas pegawai.csv diimpor dengan pola yang sama, tetapi kolom tahunMasuk melewati toInteger().
LOAD CSV WITH HEADERS FROM 'file:///pegawai.csv' AS baris
MERGE (k:Karyawan {nama: baris.nama})
SET k.divisi = baris.divisi,
k.tahunMasuk = toInteger(baris.tahunMasuk);
MATCH (k:Karyawan)
RETURN k.nama AS nama, k.divisi AS divisi, k.tahunMasuk AS tahunMasuk
ORDER BY k.nama;nama, divisi, tahunMasuk
"Agus", "Infra", 2019
"Bella", "Data", 2021
"Candra", "Produk", 2018
"Dinda", "Data", 2022
"Eri", "Infra", 2020Selisihnya terlihat pada keluaran, yaitu tahunMasuk yang kini tampil tanpa kutip karena telah tersimpan sebagai bilangan bulat.
Relasi antara karyawan dan divisi dibentuk pada tahap ketiga dengan membaca ulang berkas yang sama. Setiap baris kini mengikat dua node yang sudah ada melalui MATCH, yaitu karyawan berdasarkan kolom nama dan divisi berdasarkan kolom divisi, kemudian pola relasi :ANGGOTA_DARI dituliskan pada MERGE. Urutannya saling bergantung, karena MATCH pada tahap terakhir menuntut kedua ujung relasinya sudah berada di dalam graf.
LOAD CSV WITH HEADERS FROM 'file:///pegawai.csv' AS baris
MATCH (k:Karyawan {nama: baris.nama})
MATCH (d:Divisi {nama: baris.divisi})
MERGE (k)-[:ANGGOTA_DARI]->(d);
MATCH (k:Karyawan)-[:ANGGOTA_DARI]->(d:Divisi)
RETURN k.nama AS nama, d.nama AS divisi
ORDER BY k.nama;nama, divisi
"Agus", "Infra"
"Bella", "Data"
"Candra", "Produk"
"Dinda", "Data"
"Eri", "Infra"Keluaran memuat lima relasi, satu untuk setiap pegawai, semuanya menghubungkan node yang sudah ada.
Pemisah kolom dengan FIELDTERMINATOR#
Opsi FIELDTERMINATOR mengganti pemisah kolom baku, yaitu koma, dengan karakter lain semacam titik koma yang lazim pada berkas ekspor dari sistem lain. Berkas divisi-titik-koma.csv memuat data yang sama dengan divisi.csv, tetapi setiap kolomnya dipisahkan oleh titik koma.
kode;nama
DT;Data
IF;Infra
PR;ProdukApabila berkas semacam ini dibaca tanpa opsi pemisah, LOAD CSV menganggapnya berisi satu kolom saja, dan kunci kode tidak pernah ditemukan. Kueri berikut memperlihatkan keadaannya.
LOAD CSV WITH HEADERS FROM 'file:///divisi-titik-koma.csv' AS baris
RETURN baris.kode AS kode
ORDER BY baris.kode;kode
NULL
NULL
NULLKeluaran memuat tiga nilai NULL karena seluruh baris jatuh ke satu kolom yang bernama kode;nama, sehingga rujukan baris.kode menemui kunci yang tidak ada. Opsi FIELDTERMINATOR ';' dituliskan setelah variabel baris untuk memperbaiki pembacaan berkas yang sama.
LOAD CSV WITH HEADERS FROM 'file:///divisi-titik-koma.csv' AS baris FIELDTERMINATOR ';'
RETURN baris.kode AS kode, baris.nama AS nama
ORDER BY baris.kode;kode, nama
"DT", "Data"
"IF", "Infra"
"PR", "Produk"Kali ini kolom terpisah dengan benar dan ketiga divisi terbaca utuh, sama seperti divisi.csv. Karakter pemisah lain dapat dituliskan melalui kode Unicode empat digit, misalnya \u003B untuk titik koma. Opsi ini hanya mengubah cara kolom dibaca, berlaku dengan maupun tanpa header, sedangkan aturan kutip pada berkas tetap sama.
Muatan besar dengan subquery ter-batch#
LOAD CSV menjalankan seluruh kuerinya dalam satu transaksi, sehingga berkas dengan ratusan ribu baris berpotensi menghabiskan memori server. Penanganannya berupa subquery ter-batch CALL { … } IN TRANSACTIONS yang membagi eksekusi menjadi transaksi-transaksi kecil dengan commit (penyimpanan permanen transaksi) di antaranya. Klausa USING PERIODIC COMMIT yang dahulu menangani pembagian ini sudah tidak tersedia sejak Neo4j 5, sehingga subquery ter-batch menjadi gantinya.
Variabel baris dimasukkan ke dalam subquery melalui klausa lingkup variabel CALL (baris), sedangkan bentuk lama CALL { WITH baris … } masih diterima tetapi sudah dinyatakan deprecated, yaitu masih berjalan tanpa jaminan ketersediaan. Ukuran kelompok ditetapkan dengan IN TRANSACTIONS OF n ROWS, dan tanpanya nilai bakuannya adalah 1000 baris per transaksi. Kueri berikut mengimpor ulang pegawai.csv dengan kelompok 500 baris.
LOAD CSV WITH HEADERS FROM 'file:///pegawai.csv' AS baris
CALL (baris) {
MERGE (k:Karyawan {nama: baris.nama})
SET k.divisi = baris.divisi,
k.tahunMasuk = toInteger(baris.tahunMasuk)
} IN TRANSACTIONS OF 500 ROWS;
MATCH (k:Karyawan)
RETURN count(k) AS jumlah;jumlah
5Pada berkas lima baris ini hanya satu transaksi dalam yang terbentuk, sehingga hasilnya sama persis dengan impor tanpa subquery. Manfaatnya baru terlihat pada berkas besar, ketika ratusan ribu baris terbagi menjadi ratusan transaksi ringan. Subquery ini hanya berjalan pada implicit transaction, yaitu transaksi yang dibuka otomatis untuk satu pernyataan, sehingga Neo4j Browser menuntut awalan :auto, sedangkan cypher-shell non-interaktif menjalankannya apa adanya.
Kesalahan umum#
Empat kesalahan berikut paling sering muncul saat topik ini dipraktikkan. Setiap butir memuat gejala yang teramati beserta penyebab dan perbaikannya.
- Berkas CSV tidak berada di dalam direktori import. Kueri ditolak dengan pesan error
unable to load external resource, tanpa data yang ditulis. Perbaikannya adalah menyalin berkas ke direktori import terlebih dahulu, atau memeriksa pengaturanserver.directories.importapabila lokasinya diubah. - Kolom
tahunMasukditulis tanpatoInteger(). Propertinya tersimpan sebagai string, sehingga perbandingan dan pengurutan angka menyimpang dari nilai bilangannya. Perbaikannya adalah membungkus nilai kolom dengantoInteger()saat penulisan. - Opsi
FIELDTERMINATORterlupa pada berkas non-koma. Seluruh baris menjadi satu kolom, sehingga rujukan kolomnya bernilai NULL tanpa pesan error apa pun. Perbaikannya adalah menuliskan opsi tersebut tepat setelah variabelbaris. CALL { … } IN TRANSACTIONSdijalankan pada Neo4j Browser tanpa awalan:auto. Kueri ditolak karena subquery ini hanya diizinkan pada implicit transaction. Perbaikannya adalah mengawali kueri dengan:autoatau menjalankannya melalui cypher-shell.
Ringkasan#
- LOAD CSV membaca berkas dari direktori import server melalui URL
file:///dan mengalirkan setiap barisnya ke dalam kueri, sedangkanWITH HEADERSmenjadikan baris map berkunci nama kolom. - Pola baris-ke-node menuliskan
MERGEdengan properti kunci di dalam pola, properti lainnya melaluiSET, dantoInteger()untuk mengonversi teks menjadi bilangan. - Opsi
FIELDTERMINATORmengganti pemisah koma, sedangkan tanpanya berkas non-koma terbaca satu kolom dan rujukan kolomnya bernilai NULL. CALL (baris) { … } IN TRANSACTIONS OF n ROWSmembagi impor besar menjadi transaksi kecil dengan commit antar kelompok, menggantikanUSING PERIODIC COMMITyang sudah dihapus.
Referensi#
- LOAD CSV — Neo4j Docs — Sintaks LOAD CSV meliputi baris header, opsi FIELDTERMINATOR, direktori import, dan anjuran constraint keunikan sebelum impor.
- CALL subqueries in transactions — Neo4j Docs — Mekanisme IN TRANSACTIONS, ukuran batch baku 1000 baris, dan awalan :auto yang dituntut Neo4j Browser.
- CALL subqueries — Neo4j Docs — Klausa lingkup variabel pada subquery CALL yang menjadi cara baku mengimpor variabel dari luar.