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 _id yang 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:

js
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:

text
switched to db latihan_field
{
  acknowledged: true,
  insertedId: ObjectId('6ad38071e9e012d6c3d7cd31')
}

Baca kembali untuk melihat bahwa semua nilai tersimpan apa adanya:

js
db.profil.findOne()

Output yang Dihasilkan:

text
{
  _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:

js
db.coba.insertOne({ $harga: 100 })
db.coba.findOne()

Output yang Dihasilkan:

text
{
  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:

js
db.coba.find({ $harga: 100 })

Output yang Dihasilkan:

text
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:

js
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:

text
{
  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:

js
db.coba.insertOne({ _id: [1, 2], nama: "array" })

Output yang Dihasilkan:

text
Uncaught 
MongoServerError: The '_id' value cannot be of type array

Selain 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:

js
db.dropDatabase()

Output yang Dihasilkan:

text
{ ok: 1, dropped: 'latihan_field' }

Praktik Penamaan yang Aman #

AturanAlasan
Awali dengan huruf atau underscoreKonsisten 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 nullTidak didukung tool; karakter null ditolak mutlak
Gunakan camelCase atau snake_case secara konsistenQuery dan pemeliharaan jangka panjang tetap ringkas

Rangkuman #

  • Value field boleh tipe BSON apa pun — termasuk nested document, array, Date, Decimal128, dan null.
  • Nama field tidak boleh memuat karakter null; $ dan . diterima server 9.0 tetapi merusak query — buktinya $harga gagal dicari dan "produk.nama" kalah oleh nested document.
  • _id dipesan 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.