Validator pada Chapter sebelumnya sudah bisa menolak document tanpa harga. Namun kontrak data sungguhan butuh lebih dari itu: panjang nama, format SKU, rentang harga, hingga daftar kategori sah. Semua itu ditulis dengan operator $jsonSchema — dialek MongoDB dari JSON Schema, standar publik yang juga dipakai di luar dunia database. Setelah menguasai Chapter ini, keterampilan yang kita dapat dapat dibawa ke tool lain yang berbicara bahasa yang sama.
Bahasa $jsonSchema #
Sebuah skema $jsonSchema adalah document yang mendeskripsikan bentuk document lain. Kata kunci yang kita pakai sepanjang Section ini:
| Kata kunci | Berlaku pada | Tugas |
|---|---|---|
bsonType | semua nilai | Tipe BSON yang diizinkan |
required | object | Field yang wajib hadir |
properties | object | Aturan per field |
minimum / maximum | angka | Batas nilai terkecil/terbesar (inklusif) |
multipleOf | angka | Nilai harus kelipatan angka tertentu |
minLength / maxLength | string | Batas jumlah karakter |
pattern | string | Regex yang harus cocok |
enum | semua | Daftar nilai sah |
title / description | semua | Metadata; ikut tampil di pesan galat |
Kita pasang kontrak lengkap untuk collection produk toko online:
use latihan_validasi
db.produk.drop()
db.createCollection("produk", { validator: { $jsonSchema: {
bsonType: "object",
title: "Produk",
description: "Produk katalog toko online",
required: ["nama", "sku", "harga", "kategori"],
properties: {
nama: { bsonType: "string", minLength: 3, maxLength: 60 },
sku: { bsonType: "string", pattern: "^[A-Z]{3}-[0-9]{3}$" },
harga: { bsonType: "number", minimum: 0, maximum: 100000000, multipleOf: 500 },
kategori: { enum: ["Minuman", "Makanan", "Alat Tulis"] },
stok: { bsonType: "int", minimum: 0 }
}
} } })
db.produk.insertOne({ nama: "Kopi Gayo 250g", sku: "MIN-001", harga: 85000, kategori: "Minuman", stok: 40 })Output yang Dihasilkan:
switched to db latihan_validasi
true
{ ok: 1 }
{
acknowledged: true,
insertedId: ObjectId('6ad38fb70a61dae06594c843')
}Bedah skema di atas baris-per-baris:
bsonType: "object"di root — setiap document MongoDB memang object; menuliskannya memastikan validator menolak nilai non-object.required— empat field yang wajib hadir;stoksengaja tidak masuk daftar karena produk baru boleh belum punya stok.properties— satu blok aturan per field; field yang tidak disebut (misalnyadeskripsi) bebas diisi apa pun.titledandescription— murni metadata, tetapi ikut tercetak pada pesan galat sehingga pesan error jadi lebih mudah dipahami tim.
Batas Angka #
Tiga kata kunci angka bekerja seperti namanya. minimum dan maximum bersifat inklusif — nilai yang sama persis dengan batas tetap lolos. multipleOf memaksa nilai berupa kelipatan; untuk harga dalam rupiah, kelipatan 500 mengikuti pecahan uang yang beredar. Dua percobaan berikut melanggar keduanya:
db.produk.insertOne({ nama: "Gula Pasir 1kg", sku: "MAK-001", harga: -5000, kategori: "Makanan" })
db.produk.insertOne({ nama: "Teh Melati 100g", sku: "MIN-002", harga: 15100, kategori: "Minuman" })Output yang Dihasilkan:
Uncaught:
MongoServerError: Document failed validation
Additional information: {
failingDocumentId: ObjectId('6ad38fb70a61dae06594c844'),
details: {
operatorName: '$jsonSchema',
title: 'Produk',
description: 'Produk katalog toko online',
schemaRulesNotSatisfied: [
{
operatorName: 'properties',
propertiesNotSatisfied: [
{
propertyName: 'harga',
details: [
{
operatorName: 'minimum',
specifiedAs: { minimum: 0 },
reason: 'comparison failed',
consideredValue: -5000
}
]
}
]
}
]
}
}
Uncaught:
MongoServerError: Document failed validation
Additional information: {
failingDocumentId: ObjectId('6ad38fb70a61dae06594c845'),
details: {
operatorName: '$jsonSchema',
title: 'Produk',
description: 'Produk katalog toko online',
schemaRulesNotSatisfied: [
{
operatorName: 'properties',
propertiesNotSatisfied: [
{
propertyName: 'harga',
details: [
{
operatorName: 'multipleOf',
specifiedAs: { multipleOf: 500 },
reason: 'considered value is not a multiple of the specified value',
consideredValue: 15100
}
]
}
]
}
]
}
}Dua galat muncul berurutan: yang pertama melanggar minimum: 0 dengan consideredValue: -5000, yang kedua melanggar multipleOf: 500 — 15100 tidak habis dibagi 500. JSON Schema juga menyediakan exclusiveMinimum dan exclusiveMaximum bila batasnya tidak boleh ikut lolos.
Aturan String #
minLength dan maxLength menghitung jumlah karakter, sedangkan pattern menuntut string cocok dengan regular expression. Pola ^[A-Z]{3}-[0-9]{3}$ pada sku membaca: tiga huruf kapital, tanda hubung, tiga digit — format seperti MIN-001. Kita uji dengan SKU huruf kecil dan nama yang terlalu pendek:
db.produk.insertOne({ nama: "Kopi Tubruk 200g", sku: "min-004", harga: 12000, kategori: "Minuman" })
db.produk.insertOne({ nama: "AB", sku: "MIN-004", harga: 12000, kategori: "Minuman" })Output yang Dihasilkan:
Uncaught:
MongoServerError: Document failed validation
Additional information: {
failingDocumentId: ObjectId('6ad38fb70a61dae06594c846'),
details: {
operatorName: '$jsonSchema',
title: 'Produk',
description: 'Produk katalog toko online',
schemaRulesNotSatisfied: [
{
operatorName: 'properties',
propertiesNotSatisfied: [
{
propertyName: 'sku',
details: [
{
operatorName: 'pattern',
specifiedAs: { pattern: '^[A-Z]{3}-[0-9]{3}$' },
reason: 'regular expression did not match',
consideredValue: 'min-004'
}
]
}
]
}
]
}
}
Uncaught:
MongoServerError: Document failed validation
Additional information: {
failingDocumentId: ObjectId('6ad38fb70a61dae06594c847'),
details: {
operatorName: '$jsonSchema',
title: 'Produk',
description: 'Produk katalog toko online',
schemaRulesNotSatisfied: [
{
operatorName: 'properties',
propertiesNotSatisfied: [
{
propertyName: 'nama',
details: [
{
operatorName: 'minLength',
specifiedAs: { minLength: 3 },
reason: 'specified string length was not satisfied',
consideredValue: 'AB'
}
]
}
]
}
]
}
}Daftar Nilai Sah dengan enum #
enum membatasi nilai pada daftar tertutup — pas untuk kategori, status, atau satuan. Nilai di luar daftar ditolak walaupun tipenya benar:
db.produk.insertOne({ nama: "Roti Tawar", sku: "MAK-002", harga: 17500, kategori: "Snack" })Output yang Dihasilkan:
Uncaught:
MongoServerError: Document failed validation
Additional information: {
failingDocumentId: ObjectId('6ad38fb70a61dae06594c848'),
details: {
operatorName: '$jsonSchema',
title: 'Produk',
description: 'Produk katalog toko online',
schemaRulesNotSatisfied: [
{
operatorName: 'properties',
propertiesNotSatisfied: [
{
propertyName: 'kategori',
details: [
{
operatorName: 'enum',
specifiedAs: { enum: [ 'Minuman', 'Makanan', 'Alat Tulis' ] },
reason: 'value was not found in enum',
consideredValue: 'Snack'
}
]
}
]
}
]
}
}Perhatikan struktur galatnya: operatorName: 'enum' lengkap dengan daftar nilai sah di specifiedAs — pembaca pesan galat langsung tahu nilai mana saja yang diterima.
Sebelum menutup, bersihkan database latihan:
db.dropDatabase()Output yang Dihasilkan:
{ ok: 1, dropped: 'latihan_validasi' }Rangkuman #
$jsonSchemamengikuti standar publik JSON Schema:propertiesuntuk aturan per field,requireduntuk kehadiran,bsonTypeuntuk tipe.- Kata kunci angka:
minimum,maximum(inklusif),multipleOf; tambahkanexclusiveMinimum/exclusiveMaximumbila batas tidak boleh lolos. - Kata kunci string:
minLength,maxLength,pattern(regex). enummengunci nilai pada daftar tertutup dan pesan galatnya menampilkan daftar nilai sah.titledandescriptionhanyalah metadata, tetapi membuat pesan galat jauh lebih ramah pembaca.
Satu hal yang belum kita bedah dalam: daftar tipe apa saja yang diterima bsonType, dan apa bedanya "number" dengan "int". Itu fokus Chapter berikutnya.