Laporan mingguan untuk tim bisnis, data umpan untuk sistem ETL, atau migrasi dari spreadsheet — semua skenario itu butuh data MongoDB dalam bentuk yang bisa dibaca manusia dan aplikasi umum: JSON atau CSV. Untuk kebutuhan itulah mongoexport dan mongoimport dibuat. Keduanya sering disalahpahami sebagai alternatif backup, padahal perannya berbeda: ini alat pertukaran data, dan format yang mereka pegang kehilangan sebagian informasi tipe BSON. Chapter ini membedah format ekspornya, membuktikan kehilangan tipe secara nyata, lalu menutup dengan opsi impor untuk menangani data yang sudah ada.
Peta materi Chapter ini:
- Ekspor JSONL dan CSV:
--fields,--type=csv,--jsonArray,--jsonFormat. - Anatomi kehilangan tipe BSON lewat Extended JSON — diuji langsung di mongosh.
- Impor:
--headerlineuntuk CSV dan empat--modepenanganan duplikat.
Ekspor ke JSON dan CSV #
Sama seperti pasangan mongodump, kedua tool ini tinggal di paket MongoDB Database Tools dan dijalankan dari terminal sistem. Perintah-perintah berikut mengikuti dokumentasi resmi (lingkungan latihan kita tidak memasang paket tersebut, sehingga tidak ada klaim keluaran eksekusi):
# ekspor JSON — default: satu document per baris (JSON Lines)
mongoexport --uri="mongodb://127.0.0.1:27017/?directConnection=true" \
--db=latihan_backup --collection=buku --out=buku.jsonl
# ekspor CSV — fields wajib dinyatakan satu per satu
mongoexport --uri="mongodb://127.0.0.1:27017/?directConnection=true" \
--db=latihan_backup --collection=buku --type=csv \
--fields=judul,harga,stok --out=buku.csv
# ekspor sebagai satu array JSON (bukan per baris)
mongoexport --uri="mongodb://127.0.0.1:27017/?directConnection=true" \
--db=latihan_backup --collection=buku --jsonArray --out=buku_array.jsonBerkas JSONL hasil ekspor berbentuk seperti ini (ilustrasi format, bukan hasil eksekusi):
{"_id":1,"judul":"Bumi","harga":89000,"stok":1200000,"terbit":{"$date":"2015-01-01T00:00:00Z"},"penulis":"Tere Liye"}
{"_id":2,"judul":"Laut Bercerita","harga":98000,"stok":500000,"terbit":{"$date":"2016-10-01T00:00:00Z"},"penulis":"Leila S. Chudori"}Sedangkan CSV membuka baris pertama sebagai nama kolom, lalu menuliskan nilai apa adanya sebagai teks (ilustrasi):
judul,harga,stok
Bumi,89000,1200000
Laut Bercerita,98000,500000Opsi-opsi yang paling sering terpakai:
| Opsi | Berlaku untuk | Peran |
|---|---|---|
--type=csv (default json) | keduanya | Memilih format keluaran; CSV wajib --fields atau --fieldFile |
--fields=nama,umur | keduanya | Memilih kolom/field yang diekspor |
--jsonArray | JSON | Seluruh hasil menjadi satu array, bukan satu document per baris |
--jsonFormat=canonical | JSON | Extended JSON v2 mode canonical (default relaxed) |
--query, --sort, --limit | keduanya | Menyaring dan mengurutkan document yang diekspor |
--pretty | JSON | Keluaran berformat indentasi |
Tipe BSON di Mata JSON #
Inilah bagian yang membuat pasangan ini bukan alat backup. BSON punya tipe yang tidak ada di JSON: Date, int 32-bit, long 64-bit, Decimal128, ObjectId, hingga data biner. Mari siapkan satu document dengan tipe-tipe tersebut:
use latihan_backup
db.dropDatabase()
db.buku.insertOne({
_id: 1, judul: "Bumi", harga: 89000, stok: Long("1200000"),
terbit: ISODate("2015-01-01T00:00:00Z"), penulis: "Tere Liye"
})
db.buku.findOne()Output yang Dihasilkan:
switched to db latihan_backup
{ ok: 1, dropped: 'latihan_backup' }
{ acknowledged: true, insertedId: 1 }
{
_id: 1,
judul: 'Bumi',
harga: 89000,
stok: Long('1200000'),
terbit: ISODate('2015-01-01T00:00:00.000Z'),
penulis: 'Tere Liye'
}mongoexport menuliskan document dalam format Extended JSON v2 — dan kita bisa melihat persis bentuknya lewat EJSON.stringify() di mongosh. Mode relaxed (default mongoexport) versus canonical:
EJSON.stringify(db.buku.findOne(), null, 2)
EJSON.stringify(db.buku.findOne(), null, 2, { relaxed: false })Output yang Dihasilkan:
{
"_id": 1,
"judul": "Bumi",
"harga": 89000,
"stok": 1200000,
"terbit": {
"$date": "2015-01-01T00:00:00Z"
},
"penulis": "Tere Liye"
}
{
"_id": {
"$numberInt": "1"
},
"judul": "Bumi",
"harga": {
"$numberInt": "89000"
},
"stok": {
"$numberLong": "1200000"
},
"terbit": {
"$date": {
"$numberLong": "1420070400000"
}
},
"penulis": "Tere Liye"
}Perbedaan dua mode ini tepat di titik angka. Mode relaxed menulis stok: 1200000 sebagai angka polos — long dan int tidak bisa dibedakan lagi. Mode canonical menjaga setiap tipe lewat pembungkus $numberInt dan $numberLong. Date bertahan di kedua mode lewat $date, tetapi datanya kembali sebagai angka epoch di mode canonical. Sebagai catatan: pembungkus $date hanya hidup selama file diproses oleh tool yang memahami Extended JSON — mongoimport memahaminya, spreadsheet dan parser JSON biasa tidak.
Apa yang terjadi kalau file itu diproses sebagai JSON polos lalu dimasukkan kembali? Simulasikan dengan JSON.parse dan bandingkan tipenya:
db.bukuJson.insertOne(JSON.parse(JSON.stringify(db.buku.findOne())))
db.bukuJson.aggregate([
{ $project: {
hargaType: { $type: "$harga" },
stokType: { $type: "$stok" },
terbitType: { $type: "$terbit" }
} }
])Output yang Dihasilkan:
{ acknowledged: true, insertedId: 1 }
[ { _id: 1, hargaType: 'int', stokType: 'object', terbitType: 'string' } ]Kerusakannya nyata: terbit yang semula date menjadi string, dan stok yang semula long berubah menjadi subdocument berisi representasi internal angka 64-bit (high, low, unsigned). Hanya harga yang selamat karena kebetulan muat sebagai angka bulat kecil. Pada CSV kerusakan ini lebih total lagi — semua nilai menjadi teks, tanpa kecuali, kecuali kita mendeklarasikan tipe per kolom lewat --columnsHaveTypes saat impor.
Impor Kembali ke MongoDB #
mongoimport membaca berkas Extended JSON, CSV, atau TSV — bukan hanya buatan mongoexport, tetapi juga tool pihak ketiga:
# impor JSONL ke collection baru
mongoimport --uri="mongodb://127.0.0.1:27017/?directConnection=true" \
--db=latihan_staging --collection=buku --file=buku.jsonl
# impor CSV: baris pertama berisi nama kolom
mongoimport --uri="mongodb://127.0.0.1:27017/?directConnection=true" \
--db=latihan_staging --collection=buku_csv \
--type=csv --headerline --file=buku.csv
# timpa document yang sudah ada berdasarkan _id, sisanya disisipkan
mongoimport --uri="mongodb://127.0.0.1:27017/?directConnection=true" \
--db=latihan_staging --collection=buku \
--mode=upsert --upsertFields=_id --file=buku.jsonlOpsi --mode menentukan nasib document yang _id-nya sudah ada di target — dan --upsertFields mengubah kunci pencocokannya bila _id tidak cocok dipakai:
| Mode | Perilaku pada document yang cocok |
|---|---|
insert (default) | Galat duplikat _id; seluruh impor berhenti |
upsert | Menimpa document lama dengan versi dari file |
merge | Menggabungkan field lama dan baru menjadi satu document |
delete | Menghapus document lama yang cocok |
Untuk data hasil mongoexport, gunakan versi mongoimport yang sama atau lebih baru; data lawas format Extended JSON v1 perlu bendera --legacy.
Rangkuman #
mongoexportmenghasilkan JSONL (satu document per baris), array tunggal (--jsonArray), atau CSV (--type=csv+--fields);mongoimportmembaca semuanya kembali.- JSON keluaran memakai Extended JSON v2 — mode
relaxed(default) meratakan perbedaanint/long, modecanonicalmenjaga semuanya lewat$numberInt/$numberLong/$date. - Round-trip melalui JSON polos atau CSV merusak tipe:
Datemenjadistring,longmenjadi subdocument; CSV membuat semua nilai menjadi teks. --mode insert|upsert|merge|deleteplus--upsertFieldsmengatur penanganan duplikat saat impor.- Untuk backup penuh, tetap gunakan
mongodump—mongoexportadalah jembatan data antar sistem, bukan salinan keselamatan.