-
Type:
Improvement
-
Resolution: Fixed
-
Priority:
Major - P3
-
Affects Version/s: None
-
Component/s: Query Execution
-
None
-
Query Execution
-
Fully Compatible
-
v9.0
-
QE 2026-08-03
-
200
-
None
-
None
-
None
-
None
-
None
-
None
-
None
Overview
In the classic execution engine, computeGeoNearDistance() (src/mongo/db/exec/classic/geo_near.cpp) constructs the point-metadata Value for every candidate document on the $geoNear hot path, even when the query did not request the point location (includeLocs / addPointMeta == false). This is wasted work on the per-document distance-computation path, which is one of the hottest parts of classic $geoNear execution.
Background
For each buffered candidate, computeGeoNearDistance() loops over the extracted geometries and, on every distance improvement, executes:
if \(minDistance < 0 || nextDistance < minDistance\) {
minDistance = nextDistance;
minDistanceMetadata = Value{stored.element}; // owned copy, built unconditionally
}
The resulting minDistanceMetadata is only ever consumed later under if (nearParams.addPointMeta). When the stored geometry is a point (the common case), stored.element is a BSON array, and {{Value
{BSONElement}}} recursively materializes an owned Value array per candidate document. For a $geoNear without includeLocs (e.g. distanceField-only queries, the default), this per-document Value construction is pure overhead.
Proposed Change
Track only the winning geometry during the loop (by pointer) and build the Value once, after the loop, and only when it is actually needed:
- Replace the Value minDistanceMetadata accumulator with const StoredGeometry* minDistanceStored.
- In the loop, set minDistanceStored = &stored; instead of copying into a Value.
- At the end, keep {{setGeoNearPoint(Value
{minDistanceStored\->element}
)}} inside the existing if (nearParams.addPointMeta) guard.
This is correctness-neutral (the emitted point metadata is unchanged) and is a general win for any $geoNear that does not request includeLocs.
Prototype Data
Prototyped and measured locally on the mongo-perf Aggregation.GeoNear2dSphere / Aggregation.GeoNear2d workloads (classic engine forced, Standalone inMemory, ARM), using interleaved A/B measurement (rebuild baseline -> measure -> rebuild prototype -> measure, back-to-back) to control for host performance drift observed over the session.
Interleaved A/B, GeoNear2dSphere (ops/sec):
* Round 1: baseline 232.3 -> prototype 289.3 (+24.5%)
* Round 2: baseline 270.3 -> prototype 325.1 (+20.3%)
GeoNear2d: approximately +6%.
Correctness: a custom harness verified n<=100, the query predicate applied, distances ascending, and an exact result signature (ids + distances) identical to baseline for both 2d and 2dsphere.
For comparison, three other per-document micro-optimizations prototyped in the same session were rejected:
* absl::InlinedVector for the geometries vector: within noise (the std::set / per-geometry unique_ptr allocations dominate, not the vector).
* Extracting only the near-field subtree instead of Document::toBson(): regression (-9% / -28%), because toBson() is already O(1) when the document is trivially convertible.
* Caching RecordIdDeduplicator::getApproximateSize(): neutral (the call is already O(1)).
Only this change (deferring the Value construction) produced a large, robust improvement.
Scope of Work
* src/mongo/db/exec/classic/geo_near.cpp — defer construction of the point-metadata Value in computeGeoNearDistance() until after the min-distance loop, guarded by addPointMeta.
Acceptance Criteria
* $geoNear point-metadata output (includeLocs) is unchanged.
* No per-document Value construction occurs when addPointMeta == false.
* Existing $geoNear correctness tests pass.
Technical Notes
* The prototype patch is attached to this ticket.
* Since this is on the query hot path, a sys-perf patch is recommended before merge.