Struktur document sudah kita pahami; sekarang saatnya memperketat sekrupnya: apa saja yang boleh menjadi nama field, dan apa saja yang boleh menjadi isinya. Aturan ini terlihat sepele sampai kita menabraknya — field bernuansa $ bisa tersimpan tetapi tidak bisa dicari, dan field bernama seperti jalur nested bisa membuat query diam-diam mengembalikan hasil kosong. Chapter ini membedah aturannya sembari membuktikan setiap klaim lewat mongosh.
Peta materi Chapter ini:
- Ragam value: satu field boleh berisi tipe BSON apa pun, termasuk document dan array.
- Aturan nama field: karakter null, awalan
$, karakter titik, dan nama kosong. - Field
_idyang istimewa: dipesan, unik, dan tidak boleh diganti. - Praktik penamaan yang aman untuk jangka panjang.
Satu Field, Semua Tipe BSON #
Value sebuah field tidak terbatas pada string dan angka. Mulai dari boolean, tanggal, angka presisi tinggi, null, sampai document dan array utuh — semuanya sah sebagai isi field. Mari kita isi satu document profil dengan ragam tipe sekaligus:
use latihan_field
db.profil.insertOne({
nama: "Budi Santoso",
usia: 34,
menikah: true,
email: "[email protected]",
alamat: { kota: "Surabaya" },
hobi: ["sepak bola", "traveling"],
tanggalLahir: new Date("1992-05-17"),
gaji: NumberDecimal("7500000.00"),
catatan: null
})Output yang Dihasilkan:
switched to db latihan_field
{
acknowledged: true,
insertedId: ObjectId('6ad38071e9e012d6c3d7cd31')
}Baca kembali untuk melihat bahwa semua nilai tersimpan apa adanya:
db.profil.findOne()Output yang Dihasilkan:
{
_id: ObjectId('6ad38071e9e012d6c3d7cd31'),
nama: 'Budi Santoso',
usia: 34,
menikah: true,
email: '[email protected]',
alamat: { kota: 'Surabaya' },
hobi: [ 'sepak bola', 'traveling' ],
tanggalLahir: ISODate('1992-05-17T00:00:00.000Z'),
gaji: Decimal128('7500000.00'),
catatan: null
}Perhatikan hasilnya: alamat berupa nested document, hobi berupa array, tanggalLahir berupa ISODate, gaji berupa Decimal128, dan catatan berupa null — sembilan field, tujuh tipe berbeda, dalam satu document. Tidak ada skema yang memaksa field tertentu berisi tipe tertentu; kebebasan ini adalah sifat dasar MongoDB yang akan kita kelola dengan disiplin di Section Data Modeling.
Aturan Nama Field #
Berbeda dengan value yang bebas, nama field punya batas. Aturan yang dijalankan server secara mutlak hanya satu: nama field tidak boleh mengandung karakter null — percobaan menyimpannya ditolak mentah oleh serializer BSON di sisi klien. Selebihnya, MongoDB 5.0 dan yang lebih baru (termasuk versi 9.0 yang kita pakai) secara teknis menerima nama field berawalan $ dan nama field yang mengandung titik. “Menerima” di sini bukan berarti aman — keduanya tetap kuat dilarang dalam praktik, dan berikut alasannya yang bisa kita buktikan.
Field Berawalan $ Menyimpan tapi Mengunci #
Coba simpan document dengan field $harga:
db.coba.insertOne({ $harga: 100 })
db.coba.findOne()Output yang Dihasilkan:
{
acknowledged: true,
insertedId: ObjectId('6ad38071e9e012d6c3d7cd32')
}
{ _id: ObjectId('6ad38071e9e012d6c3d7cd32'), '$harga': 100 }Insert berhasil dan field tersimpan sebagai '$harga': 100 — server memang mengizinkannya. Masalah muncul begitu kita mencari document itu kembali:
db.coba.find({ $harga: 100 })Output yang Dihasilkan:
Uncaught
MongoServerError[BadValue]: unknown top level operator: $harga. If you have a field name that starts with a '$' symbol, consider using $getField or $setField.MongoDB menolak query tersebut karena setiap nama yang diawali $ di dalam query dianggap operator ($set, $inc, $match, dan kawan-kawannya). Awalan ini juga dipakai server untuk keperluan internal: perintah yang kita kirim lewat db.runCommand() diantar ke collection internal <database>.$cmd, dan itulah alasan nama collection pengguna dilarang memuat $ (lihat galat produk$stok di Chapter Database dan Collections). Data yang tersimpan tetap bisa diakses lewat operator $getField di aggregation, tetapi jauh lebih sederhana menghindari awalan $ sejak awal.
Titik dalam Nama Field Bertabrakan dengan Nested #
Karakter titik punya makna khusus: alamat.kota berarti “field kota di dalam field alamat”. Simpan dua document — satu dengan nama field literal "produk.nama", satu dengan nested document produk.nama — lalu cari keduanya:
db.barang.insertOne({ "produk.nama": "Kopi Literal" })
db.barang.insertOne({ produk: { nama: "Kopi Nested" } })
db.barang.find({ "produk.nama": "Kopi Nested" })
db.barang.find({ "produk.nama": "Kopi Literal" })Output yang Dihasilkan:
{
acknowledged: true,
insertedId: ObjectId('6ad38071e9e012d6c3d7cd33')
}
{
acknowledged: true,
insertedId: ObjectId('6ad38071e9e012d6c3d7cd34')
}
[
{
_id: ObjectId('6ad38071e9e012d6c3d7cd34'),
produk: { nama: 'Kopi Nested' }
}
]Query pertama mengembalikan document nested, sedangkan query kedua mengembalikan hasil kosong walau document dengan field literal "produk.nama" ada di collection — blok terakhir di atas tidak mencetak apa pun. Query selalu menafsirkan titik sebagai jalur ke nested document, sehingga field literal ber-titik praktis tidak bisa dicari. Kesimpulannya: butuh struktur bertingkat? Gunakan nested document sungguhan, bukan titik di nama field.
Field _id yang Istimewa #
Nama _id dipesan MongoDB sebagai primary key: nilainya wajib unik di dalam collection, tidak bisa diubah setelah document tersimpan (immutable), dan boleh bertipe apa pun kecuali array dan regex. Dua pengecualian itu dijalankan langsung oleh server:
db.coba.insertOne({ _id: [1, 2], nama: "array" })Output yang Dihasilkan:
Uncaught
MongoServerError: The '_id' value cannot be of type arraySelain pengecualian tersebut, _id bebas kita isi — string seperti "SKU-001", angka, sampai nested document semuanya sah, dan Chapter ObjectId berikutnya membahas pilihan tersebut secara khusus. Satu document juga tidak boleh memiliki nama field kembar; object JavaScript di mongosh sudah mencegahnya secara alami.
Sebagai penutup, bersihkan database latihan:
db.dropDatabase()Output yang Dihasilkan:
{ ok: 1, dropped: 'latihan_field' }Praktik Penamaan yang Aman #
| Aturan | Alasan |
|---|---|
| Awali dengan huruf atau underscore | Konsisten dengan konvensi umum dan aman di semua driver |
Hindari awalan $ | Terreserve untuk operator query dan namespace internal seperti $cmd |
Hindari titik . | Bertabrakan dengan dot notation untuk nested document |
| Hindari nama kosong dan karakter null | Tidak didukung tool; karakter null ditolak mutlak |
Gunakan camelCase atau snake_case secara konsisten | Query dan pemeliharaan jangka panjang tetap ringkas |
Rangkuman #
- Value field boleh tipe BSON apa pun — termasuk nested document, array,
Date,Decimal128, dannull. - Nama field tidak boleh memuat karakter null;
$dan.diterima server 9.0 tetapi merusak query — buktinya$hargagagal dicari dan"produk.nama"kalah oleh nested document. _iddipesan sebagai primary key: unik, immutable, bertipe apa pun kecuali array dan regex.- Disiplin penamaan (
camelCase, tanpa$/./kosong) menjaga data tetap mudah dicari oleh semua driver dan tool.
Nama dan nilai sudah kita kuasai. Chapter berikutnya membedah ObjectId — nilai _id bawaan yang otomatis tercipta di setiap insert.