Toko buku kita kini punya cabang fisik di beberapa kota, dan pertanyaan pembaca berbunyi: “tampilkan cabang terdekat dari posisi saya”. Query harga atau kategori tidak bisa menjawabnya — yang dibutuhkan adalah pencarian berbasis koordinat. Geospatial index menjawab kebutuhan itu: index khusus yang menyimpan titik lokasi sehingga MongoDB bisa mencari document “dekat dengan” atau “di dalam area” tertentu secara efisien. Chapter ini fokus pada penyiapan indexnya; operator query lokasi dibahas tuntas di Section Search dan Geospatial.
Peta materi Chapter ini:
- Menyimpan lokasi sebagai GeoJSON
Pointdan membuat index2dsphere. - Validasi rentang longitude/latitude yang dijaga MongoDB.
- Perbedaan index
2d(koordinat lama) dan2dsphere(GeoJSON). - Pratinjau query
$neardan$geoWithin.
Menyimpan Lokasi sebagai GeoJSON Point #
Format penyimpanan yang direkomendasikan adalah GeoJSON — object dengan field type dan coordinates. Untuk satu titik, type bernilai "Point" dan coordinates berisi array [longitude, latitude]. Mari simpan empat cabang toko buku:
use latihan_index
db.cabang.drop()
db.cabang.insertMany([
{ nama: "Toko Buku Mentari", kota: "Jakarta", lokasi: { type: "Point", coordinates: [106.8456, -6.2088] } },
{ nama: "Toko Buku Saguling", kota: "Bandung", lokasi: { type: "Point", coordinates: [107.6098, -6.9147] } },
{ nama: "Toko Buku Tunas", kota: "Surabaya", lokasi: { type: "Point", coordinates: [112.7508, -7.2575] } },
{ nama: "Toko Buku Gadjah", kota: "Yogyakarta", lokasi: { type: "Point", coordinates: [110.3791, -7.7956] } }
])
db.cabang.createIndex({ lokasi: "2dsphere" })Output yang Dihasilkan:
switched to db latihan_index
true
{
acknowledged: true,
insertedIds: {
'0': ObjectId('6ad3bc1f8a64345aaf597b6b'),
'1': ObjectId('6ad3bc1f8a64345aaf597b6c'),
'2': ObjectId('6ad3bc1f8a64345aaf597b6d'),
'3': ObjectId('6ad3bc1f8a64345aaf597b6e')
}
}
lokasi_2dsphereDua hal pada blok tersebut perlu digarisbawahi. Pertama, createIndex({ lokasi: "2dsphere" }) memakai string "2dsphere" sebagai “arah” — bukan 1 atau -1 — karena tipe index ini bukan B-tree biasa melainkan struktur berbasis grid bola bumi. Kedua, urutan koordinatnya longitude dulu, latitude kemudian, mengikuti konvensi GeoJSON internasional.
Validasi Rentang Koordinat #
Index 2dsphere aktif menjaga mutu data: setiap insert dan update pada field ter-index divalidasi terhadap rentang sah bumi — longitude harus berada di [-180, 180] dan latitude di [-90, 90]. Coba sisipkan titik dengan longitude 200:
try {
db.cabang.insertOne({ nama: "Cabang Uji", kota: "Fiktif", lokasi: { type: "Point", coordinates: [200, -6.2] } })
} catch (e) {
print("Kode galat: " + e.code)
print("Pesan: " + e.message)
}Output yang Dihasilkan:
Kode galat: 510
Pesan: Could not extract geo keys at path 'lokasi' for document with _id ObjectId('6ad3bc1f8a64345aaf597b6f'): Longitude/latitude is out of bounds, lng: 200 lat: -6.2Galat berkode 510 itu muncul pada saat insert — bukan saat query — sehingga data rusak tertahan di pintu masuk. Perlu dicatat: validasi rentang hanya menolak angka di luar batas; koordinat yang sah namun salah tempat (misalnya Jakarta yang tertukar dengan Samudra Hindia) tetap lolos. Pembersihan data lama sebelum migrasi tetap tanggung jawab aplikasi.
2d atau 2dsphere #
MongoDB menyediakan dua tipe index geospatial. Index 2d bekerja pada pasangan koordinat polos ([x, y] langsung di field, tanpa pembungkus GeoJSON) dan menghitung jarak pada bidang datar. Index 2dsphere bekerja pada GeoJSON dan menghitung jarak di permukaan bola bumi:
db.petaLama.drop()
db.petaLama.insertMany([
{ nama: "Rak Fiksi", pos: [106.84, -6.20] },
{ nama: "Rak Puisi", pos: [106.85, -6.21] }
])
db.petaLama.createIndex({ pos: "2d" })Output yang Dihasilkan:
true
{
acknowledged: true,
insertedIds: {
'0': ObjectId('6ad3bc1f8a64345aaf597b70'),
'1': ObjectId('6ad3bc1f8a64345aaf597b71')
}
}
pos_2d| Aspek | 2d | 2dsphere |
|---|---|---|
| Bentuk data | Array [x, y] polos | GeoJSON (Point, LineString, Polygon) |
| Perhitungan jarak | Bidang datar (Euclidean) | Permukaan bola bumi |
| Rentang koordinat | Wajib [-180, 180] × [-90, 90] | Sama, plus validasi struktur GeoJSON |
Operator $near | Mendukung (jarak dalam satuan data) | Mendukung ($maxDistance dalam meter) |
| Cocok untuk | Data lama, peta ruangan/rak | Lokasi di bumi — pilihan default |
Untuk aplikasi lokasi nyata, gunakan 2dsphere — jaraknya dihitung dalam meter yang akurat mengikuti kelengkungan bumi. Index 2d umumnya dijumpai pada sistem lama atau pemetaan ruangan seperti posisi rak di dalam toko pada blok di atas.
Pratinjau Query $near dan $geoWithin #
Index yang baru dibuat langsung siap melayani query lokasi. Operator $near mengembalikan document terurut dari yang terdekat, dan $maxDistance membatasinya dalam satuan meter:
db.cabang.find(
{ lokasi: { $near: { $geometry: { type: "Point", coordinates: [106.8456, -6.2088] }, $maxDistance: 200000 } } },
{ _id: 0, nama: 1, kota: 1 }
)Output yang Dihasilkan:
[
{ nama: 'Toko Buku Mentari', kota: 'Jakarta' },
{ nama: 'Toko Buku Saguling', kota: 'Bandung' }
]Hasilnya terurut dari terdekat: Mentari Jakarta, lalu Saguling Bandung — keduanya dalam radius 200 km dari titik pencarian. Operator kedua, $geoWithin, memeriksa keanggotaan di dalam area tanpa pengurutan hasil:
db.cabang.find(
{ lokasi: { $geoWithin: { $centerSphere: [[106.8456, -6.2088], 0.05] } } },
{ _id: 0, nama: 1, kota: 1 }
)Output yang Dihasilkan:
[
{ nama: 'Toko Buku Saguling', kota: 'Bandung' },
{ nama: 'Toko Buku Mentari', kota: 'Jakarta' }
]Perhatikan urutan hasilnya berbeda dan tidak berbasis jarak — $geoWithin sekadar memeriksa masuk atau tidaknya titik ke dalam lingkaran beradius 0.05 radian (sekitar 318 km) dari Jakarta. Satu fakta penting: query $near mensyaratkan index geospatial — tanpa index, MongoDB menolak query tersebut dengan galat unable to find index for $geoNear query.
Rangkuman #
- Simpan lokasi sebagai GeoJSON
Pointdengan urutan[longitude, latitude], lalu buat index dengancreateIndex({ lokasi: "2dsphere" }). - Index
2dspherememvalidasi rentang koordinat saat insert/update; pelanggaran ditolak dengan galat kode510. - Index
2duntuk koordinat polos dan jarak bidang datar;2dsphereuntuk GeoJSON dan jarak permukaan bumi dalam meter. $nearmengembalikan hasil terurut jarak dan wajib didukung index;$geoWithinmemeriksa keanggotaan area tanpa pengurutan.