Prompt 20 · Lesson 02 · Geospatial index families
2dsphere vs 2d Indexes and Choosing the Right Geometry Model
Choose spherical or planar indexing from the geometry, not from familiarity with an index name.
Learning objectives
Choose 2dsphere for Earth-like spherical geometry and 2d for genuinely planar legacy-coordinate workloads.
Observe index metadata, sparse behavior, compound-index restrictions, covered-query restrictions, and MongoDB 8.3 2dsphere index version 4.
Explain why 2d is unsuitable for global geography and why 2dsphere can also index legacy pairs when migration requires it.
Use a warehouse-floor example to show a legitimate 2d workload without confusing coordinate units with meters on Earth.
Diagnose geometry/index mismatches and reject non-geometry values safely.
This lesson pins MongoDB Community Server
8.3.8 using
mongodb/mongodb-community-server:8.3.8-ubuntu2204-slim, mongosh 2.10.0, and PyMongo
4.17.0 where client measurement is useful. The
mandatory lab is a disposable loopback-only standalone on port
27166 named atlasmart-ch20-l2.
Authentication and TLS are disabled only for this isolated
learning container. Default read/write concern and primary read
preference apply.
FCV is observed and never changed. Atlas,
Search, Vector Search, KMS, and Enterprise Advanced are not
required. MongoDB 8.3 creates new 2dsphere indexes
as index version 4; downgrading FCV below 8.3
requires dropping those version-4 indexes first. Product labs
were not executed in this generation environment, so distance,
explain, index-size, and latency evidence that depends on
runtime must be measured locally rather than copied as invented
output. The lesson intentionally uses two different domains:
geographic store locations for 2dsphere and a
synthetic warehouse floor for 2d.
1. Index choice follows the geometry model
A geospatial index is not merely a performance toggle. It
determines what geometry MongoDB uses.
2dsphere interprets data on an earth-like sphere
and supports GeoJSON plus legacy pairs.
2d interprets legacy coordinate pairs on a flat
Euclidean plane and is intended for local planar coordinates.
Using 2d for global longitude/latitude can produce
wrong results around the poles or wrap-around boundaries.
| Property | 2dsphere | 2d |
|---|---|---|
| Geometry | spherical / Earth-like | flat Euclidean plane |
| Primary representation | GeoJSON; legacy pairs also accepted | legacy coordinate pairs |
| Sparse behavior | always sparse | always sparse |
| Compound structure | multiple geo + non-geo fields permitted | one location field first + one additional field |
| Covered queries | geospatial indexes cannot cover | 2d compound indexes have limited cover capability; treat geospatial result fetches explicitly |
| Shard key | cannot itself be a shard key | cannot itself be a shard key |
2. Build both index families and inspect them
docker rm -f atlasmart-ch20-l2 2>/dev/null || truedocker volume rm atlasmart-ch20-l2-data 2>/dev/null || truedocker run -d --name atlasmart-ch20-l2 \ -p 127.0.0.1:27166:27017 \ -v atlasmart-ch20-l2-data:/data/db \ mongodb/mongodb-community-server:8.3.8-ubuntu2204-slim \ --bind_ip_alldocker exec atlasmart-ch20-l2 mongosh --quiet --eval 'printjson(db.adminCommand({buildInfo:1}).version);printjson(db.adminCommand({getParameter:1,featureCompatibilityVersion:1}).featureCompatibilityVersion);'
const d=db.getSiblingDB("atlasmart");d.stores_ch20_l2.drop(); d.floor_bins_ch20_l2.drop();d.stores_ch20_l2.insertMany([ {_id:"s1",tenantId:"tenant-a",open:true,location:{type:"Point",coordinates:[49.8671,40.4093]}}, {_id:"s2",tenantId:"tenant-a",open:false,location:{type:"Point",coordinates:[49.8971,40.4193]}}, {_id:"s3",tenantId:"tenant-b",open:true,location:{type:"Point",coordinates:[49.8371,40.3993]}}]);d.stores_ch20_l2.createIndex({tenantId:1,location:"2dsphere"},{name:"tenant_location_2dsphere"});d.floor_bins_ch20_l2.insertMany([ {_id:"b1",zone:"cold",xy:[10,10]}, {_id:"b2",zone:"cold",xy:[18,13]}, {_id:"b3",zone:"ambient",xy:[80,75]}, {_id:"b4",zone:"ambient",xy:[90,88]}]);d.floor_bins_ch20_l2.createIndex({xy:"2d",zone:1},{name:"xy_2d_zone",min:0,max:100});printjson(d.stores_ch20_l2.getIndexes());printjson(d.floor_bins_ch20_l2.getIndexes());
On MongoDB 8.3, the first collection should expose a version-4
2dsphere index. Both geospatial families are sparse
by definition: a document whose geospatial field is missing,
null, or empty does not contribute a geospatial index entry. For
compound indexes, the geospatial field controls whether the
document appears in the index.
3. Compare spherical meters with planar units
const d=db.getSiblingDB("atlasmart");print("spherical GeoJSON: meters");printjson(d.stores_ch20_l2.find({location:{$near:{$geometry:{type:"Point",coordinates:[49.8671,40.4093]},$maxDistance:5000}}},{_id:1,location:1}).toArray());print("planar warehouse floor: coordinate units");printjson(d.floor_bins_ch20_l2.find({xy:{$near:[12,12],$maxDistance:20}},{_id:1,zone:1,xy:1}).toArray());
The first query's GeoJSON distance bound is meters. The second query operates in whatever planar units the floor model defines. Calling the second value “meters” would be an application contract, not something MongoDB infers. This distinction prevents one of the most common geospatial bugs: attaching a physical unit to an untyped coordinate system.
4. Controlled failures and restrictions
const d=db.getSiblingDB("atlasmart");try { d.stores_ch20_l2.insertOne({_id:"bad",tenantId:"tenant-a",location:"Baku"}); }catch(e){ print("expected indexed-geometry failure",e.code,e.codeName); }print("bad count",d.stores_ch20_l2.countDocuments({_id:"bad"}));
Geospatial indexes have additional boundaries: they cannot be
the shard key, and geospatial indexes cannot cover ordinary
geospatial queries because MongoDB needs geometry/document
information beyond index-only projection. A
2d index does not support non-simple collation; if
its collection has another default collation, index creation
must explicitly use simple collation. Do not generalize ordinary
B-tree index rules blindly to geospatial index families.
5. Production judgment
Use 2dsphere for store discovery, deliveries, fleet
points, service polygons, and other Earth geometry. Use
2d when the coordinate system is deliberately
planar: a warehouse floor, game map, industrial drawing, or
synthetic x/y space. Document coordinate units in the schema/API
contract and test them at boundaries.
The index itself has write/storage cost. Before adding a second
geospatial index, verify the distinct query need; if a
collection has multiple geospatial indexes,
$geoNear may require an explicit key.
In MongoDB 8.3, include version-4 2dsphere indexes in downgrade
planning.
Bridge. Lesson 3 uses the chosen geometry to compare nearest-neighbor, containment, and intersection predicates.
docker rm -f atlasmart-ch20-l2 2>/dev/null || truedocker volume rm atlasmart-ch20-l2-data 2>/dev/null || true
Check your understanding
- When is 2d the right choice?
- Are 2dsphere and 2d indexes sparse?
- What does $maxDistance mean for a GeoJSON $near query?
- Can a geospatial index itself be used as a shard key?
- What is new about 2dsphere indexes in MongoDB 8.3?
Review the answers
1. For a genuinely planar coordinate system represented as legacy pairs, not for general Earth geography.
2. Yes. Both are always sparse and ignore the sparse option.
3. Meters.
4. No, though a sharded collection can have a geospatial index when another field is the shard key.
5. New 2dsphere indexes default to index version 4, which matters for FCV downgrade planning.
Authoritative references
- Geospatial Queries — GeoJSON versus legacy coordinates, WGS84, operators, and MongoDB 8.2 representation precedence.
- GeoJSON Objects — supported geometry forms and longitude/latitude ordering.
- 2dsphere Indexes — spherical indexing, sparse behavior, compound rules, and version 4 in MongoDB 8.3.
- 2d Indexes — flat Euclidean indexing for legacy coordinate pairs and compound limitations.
- Geospatial Index Restrictions — covered-query, collation, shard-key, supported-data, and multiple-index restrictions.
- $geometry — EPSG:4326 default coordinate-reference behavior and GeoJSON geometry syntax.
- $near — nearest-first query semantics and index requirements.
- $nearSphere — spherical proximity, distance units, validation, and sorting behavior.
- $geoWithin — containment predicates and unsorted spatial filtering.
- $geoIntersects — intersection predicates for GeoJSON geometry.
- $geoNear — first-stage/index rules, distanceField/key/query options, and distance units.
- $centerSphere — spherical-cap radius semantics in radians.
- Explain Results — executionStats interpretation and plan-format caveats.
- MongoDB 8.3 Release Notes — 2dsphere index version 4 and current 8.3 behavior.
- mongosh Release Notes — mongosh 2.10.0 baseline.
- PyMongo Release Notes — PyMongo 4.17 driver baseline.