Chapter BSON Types memetakan seluruh tipe yang bisa disimpan BSON — empat tipe angka, Date, ObjectId, array, hingga BinData. Sekarang kita belajar memaksa server menolak tipe yang salah lewat kata kunci bsonType. Ini penjaga pintu paling dasar sekaligus paling sering dijumpai: sebagian besar data rusak bermula dari field yang tipenya tidak sesuai harapan.

“number” versus Tipe Angka Spesifik #

bsonType mengenal dua tingkat ketat untuk angka. Nilai "number" menerima semua tipe angka BSON, sedangkan "int", "long", "double", dan "decimal" menuntut tipe spesifik tersebut:

Nilai bsonTypeYang diterimaKapan dipakai
"number"double, int, long, decimalField angka umum
"double"Hanya double 64-bitNilai hasil pengukuran
"int"Hanya Int32Bilangan bulat kecil: stok, jumlah
"long"Hanya Int64Bilangan bulat besar
"decimal"Hanya Decimal128Uang presisi tinggi

Ingat perilaku angka literal di mongosh dari Chapter BSON Types: bilangan yang bernilai bulat dan muat rentang Int32 tersimpan sebagai int — termasuk yang ditulis dengan titik desimal seperti 2.0 — sedangkan nilai pecahan seperti 0.25 dan bilangan di luar rentang Int32 tersimpan sebagai double. Perilaku ini menentukan lolos atau tidaknya sebuah insert. Kita buat collection dengan campuran tipe ketat:

js
use latihan_validasi
db.produk.drop()
db.createCollection("produk", { validator: { $jsonSchema: {
  bsonType: "object",
  required: ["nama", "sku", "harga", "stok"],
  properties: {
    _id: { bsonType: "objectId" },
    nama: { bsonType: "string" },
    sku: { bsonType: "string" },
    harga: { bsonType: "number", minimum: 0 },
    stok: { bsonType: "int", minimum: 0 },
    berat: { bsonType: "double", minimum: 0 },
    tanggal_ditambahkan: { bsonType: "date" },
    aktif: { bsonType: "bool" },
    tag: { bsonType: "array" },
    deskripsi: { bsonType: ["string", "null"], maxLength: 200 }
  }
} } })
db.produk.insertOne({
  nama: "Kopi Gayo 250g",
  sku: "MIN-001",
  harga: 85000,
  stok: 40,
  berat: 0.25,
  tanggal_ditambahkan: new Date("2026-01-10"),
  aktif: true,
  tag: ["kopi", "arabika"],
  deskripsi: null
})

Output yang Dihasilkan:

text
switched to db latihan_validasi
true
{ ok: 1 }
{
  acknowledged: true,
  insertedId: ObjectId('6ad3901510c4756be30e78cf')
}

stok: 40 lolos karena literal tersebut memang int; berat: 0.25 lolos karena nilai pecahan menjadi double. Sekarang kita balik kadarnya — stok diberi desimal, berat diberi bilangan bulat:

js
db.produk.insertOne({ nama: "Teh Melati 100g", sku: "MIN-002", harga: 15000, stok: 25.5 })
db.produk.insertOne({ nama: "Teh Melati 100g", sku: "MIN-002", harga: 15000, stok: 25, berat: 250 })

Output yang Dihasilkan:

text
Uncaught:

MongoServerError: Document failed validation
Additional information: {
  failingDocumentId: ObjectId('6ad3901510c4756be30e78d0'),
  details: {
    operatorName: '$jsonSchema',
    schemaRulesNotSatisfied: [
      {
        operatorName: 'properties',
        propertiesNotSatisfied: [
          {
            propertyName: 'stok',
            details: [
              {
                operatorName: 'bsonType',
                specifiedAs: { bsonType: 'int' },
                reason: 'type did not match',
                consideredValue: 25.5,
                consideredType: 'double'
              }
            ]
          }
        ]
      }
    ]
  }
}
Uncaught:

MongoServerError: Document failed validation
Additional information: {
  failingDocumentId: ObjectId('6ad3901510c4756be30e78d1'),
  details: {
    operatorName: '$jsonSchema',
    schemaRulesNotSatisfied: [
      {
        operatorName: 'properties',
        propertiesNotSatisfied: [
          {
            propertyName: 'berat',
            details: [
              {
                operatorName: 'bsonType',
                specifiedAs: { bsonType: 'double' },
                reason: 'type did not match',
                consideredValue: 250,
                consideredType: 'int'
              }
            ]
          }
        ]
      }
    ]
  }
}

Kedua insert ditolak dengan alasan yang sama-sama type did not match: 25.5 adalah double sehingga gagal di stok yang menuntut int, dan 250 adalah int sehingga gagal di berat yang menuntut double. Pesan galatnya menyebut consideredType — tipe aktual nilai yang kita kirim — sehingga penyebabnya langsung terlihat.

“number” Menerima Semua Tipe Angka #

Berbeda dengan dua field ketat tadi, harga bertipe "number" menerima keempat tipe angka BSON sekaligus. Empat insert berikut semuanya lolos:

js
db.produk.insertOne({ nama: "Teh Melati 100g", sku: "MIN-002", harga: 15000, stok: 25 })
db.produk.insertOne({ nama: "Gula Pasir 1kg", sku: "MAK-001", harga: NumberLong("15000"), stok: 25 })
db.produk.insertOne({ nama: "Susu UHT 1L", sku: "MIN-003", harga: NumberDecimal("19500.00"), stok: 25 })
db.produk.insertOne({ nama: "Kopi Tubruk 200g", sku: "MIN-004", harga: 12000.5, stok: 25 })

Output yang Dihasilkan:

text
{
  acknowledged: true,
  insertedId: ObjectId('6ad3901510c4756be30e78d2')
}
{
  acknowledged: true,
  insertedId: ObjectId('6ad3901510c4756be30e78d3')
}
{
  acknowledged: true,
  insertedId: ObjectId('6ad3901510c4756be30e78d4')
}
{
  acknowledged: true,
  insertedId: ObjectId('6ad3901510c4756be30e78d5')
}

Empat nilai harga di atas masing-masing tersimpan sebagai int, long, decimal, dan double — dan semuanya sah bagi "number". Aturan praktisnya: pakai "number" saat aplikasi menulis lewat banyak jalur dengan tipe angka berbeda; pakai tipe spesifik saat konsistensi tipe benar-benar dibutuhkan, misalnya "decimal" untuk kolom uang.

Tipe Non-angka #

Sisanya langsung menyebut namanya: "string", "bool", "date", "objectId", "array", dan "object". Dua kasus yang paling sering terjadi: tanggal yang dikirim sebagai string dari aplikasi, dan _id custom yang bukan ObjectId:

js
db.produk.insertOne({ nama: "Teh Melati 100g", sku: "MIN-002", harga: 15000, stok: 25, tanggal_ditambahkan: "2026-02-01" })
db.produk.insertOne({ _id: "PRODUK-001", nama: "Teh Melati 100g", sku: "MIN-002", harga: 15000, stok: 25 })

Output yang Dihasilkan:

text
Uncaught:

MongoServerError: Document failed validation
Additional information: {
  failingDocumentId: ObjectId('6ad3901510c4756be30e78d6'),
  details: {
    operatorName: '$jsonSchema',
    schemaRulesNotSatisfied: [
      {
        operatorName: 'properties',
        propertiesNotSatisfied: [
          {
            propertyName: 'tanggal_ditambahkan',
            details: [
              {
                operatorName: 'bsonType',
                specifiedAs: { bsonType: 'date' },
                reason: 'type did not match',
                consideredValue: '2026-02-01',
                consideredType: 'string'
              }
            ]
          }
        ]
      }
    ]
  }
}
Uncaught:

MongoServerError: Document failed validation
Additional information: {
  failingDocumentId: 'PRODUK-001',
  details: {
    operatorName: '$jsonSchema',
    schemaRulesNotSatisfied: [
      {
        operatorName: 'properties',
        propertiesNotSatisfied: [
          {
            propertyName: '_id',
            details: [
              {
                operatorName: 'bsonType',
                specifiedAs: { bsonType: 'objectId' },
                reason: 'type did not match',
                consideredValue: 'PRODUK-001',
                consideredType: 'string'
              }
            ]
          }
        ]
      }
    ]
  }
}

Insert pertama gagal karena "2026-02-01" bertipe string, bukan date — new Date("2026-02-01") barulah lolos. Insert kedua gagal karena _id: "PRODUK-001" bertipe string padahal validator menuntut objectId. Perhatikan failingDocumentId pada galat kedua: nilainya persis string 'PRODUK-001' yang kita kirim.

Tipe Ganda: string atau null #

bsonType juga menerima array tipe — nilai dianggap sah bila cocok dengan salah satunya. Pola paling umum adalah ["string", "null"] untuk field opsional yang boleh “ada tapi kosong”. Insert pertama kita sudah membuktikan deskripsi: null lolos; sekarang kita kirim nilai yang bukan string dan bukan null, plus boolean berupa teks:

js
db.produk.insertOne({ nama: "Teh Melati 100g", sku: "MIN-002", harga: 15000, stok: 25, deskripsi: 42 })
db.produk.insertOne({ nama: "Teh Melati 100g", sku: "MIN-002", harga: 15000, stok: 25, aktif: "ya" })

Output yang Dihasilkan:

text
Uncaught:

MongoServerError: Document failed validation
Additional information: {
  failingDocumentId: ObjectId('6ad3901510c4756be30e78d7'),
  details: {
    operatorName: '$jsonSchema',
    schemaRulesNotSatisfied: [
      {
        operatorName: 'properties',
        propertiesNotSatisfied: [
          {
            propertyName: 'deskripsi',
            details: [
              {
                operatorName: 'bsonType',
                specifiedAs: { bsonType: [ 'string', 'null' ] },
                reason: 'type did not match',
                consideredValue: 42,
                consideredType: 'int'
              }
            ]
          }
        ]
      }
    ]
  }
}
Uncaught:

MongoServerError: Document failed validation
Additional information: {
  failingDocumentId: ObjectId('6ad3901510c4756be30e78d8'),
  details: {
    operatorName: '$jsonSchema',
    schemaRulesNotSatisfied: [
      {
        operatorName: 'properties',
        propertiesNotSatisfied: [
          {
            propertyName: 'aktif',
            details: [
              {
                operatorName: 'bsonType',
                specifiedAs: { bsonType: 'bool' },
                reason: 'type did not match',
                consideredValue: 'ya',
                consideredType: 'string'
              }
            ]
          }
        ]
      }
    ]
  }
}

Bedah dua hasil di atas:

  1. deskripsi: 42 ditolak — 42 bukan string dan bukan null.
  2. aktif: "ya" ditolak — bool hanya menerima true atau false, string tidak pernah otomatis dikonversi.

Bersihkan database latihan:

js
db.dropDatabase()

Output yang Dihasilkan:

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

Rangkuman #

  • "number" menerima double, int, long, dan decimal sekaligus; "int", "long", "double", "decimal" menuntut tipe persisnya.
  • Literal di mongosh: bilangan bulat muat Int32 menjadi int, berkoma menjadi double — ini menentukan lolos tidaknya validator ketat.
  • Tipe non-angka ditulis langsung: "string", "bool", "date", "objectId", "array", "object".
  • Pesan galat type did not match menyertakan consideredType — tipe aktual nilai yang dikirim.
  • bsonType menerima array seperti ["string", "null"] untuk field opsional yang boleh kosong.

Materi selanjutnya membahas kata kunci yang paling sering dipakai bersama bsonType: required — dan jebakan null yang menyertainya.