How do geospatial Near queries work in a Spring Data MongoDB repository — the Near/Within keywords, index requirement, and GeoResults?
answer
- @GeoSpatialIndexed 2dsphere — index mandatory
- findByLocationNear(Point, Distance) -> $near, sorted
- Metrics.KILOMETERS => spherical/2dsphere
- Within(Circle/Box/Polygon) -> $geoWithin, unordered
- GeoResults<T> carries per-hit Distance; GeoJSON = [lng, lat]
basics
~20 sYou add a derived method with the Near keyword taking a Point and a Distance, e.g. findByLocationNear(Point p, Distance d). The location field needs a geospatial index (@GeoSpatialIndexed), and returning GeoResults gives each hit's distance.
solid answer
~40 sFor proximity search, the document has a location field indexed with @GeoSpatialIndexed (or a 2dsphere index). A derived method uses Near: findByLocationNear(Point point, Distance distance) generates a $near (or $geoNear) query returning documents ordered by distance up to that radius. Passing a Distance with Metrics.KILOMETERS makes it spherical (2dsphere); a plain Distance uses flat 2d. Within keyword uses $geoWithin over a Circle/Box/Polygon/GeoJson shape. Return List<T> for plain results, or GeoResults<T>/GeoPage<T> so each GeoResult carries the computed distance from the reference point. You can pass a Range<Distance> for min/max radius. Without a geospatial index, MongoDB rejects $near — the index is mandatory. Use Point/Distance/Metrics/Circle/Box/Polygon from Spring Data's geo package and prefer GeoJSON + 2dsphere for real-world lat/long.
code
java · 23 lines@Document
public class Restaurant {
@Id private String id;
@GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE)
private GeoJsonPoint location; // coordinates [longitude, latitude]
}
public interface RestaurantRepository extends MongoRepository<Restaurant, String> {
// sorted by distance, each result carries its Distance
GeoResults<Restaurant> findByLocationNear(Point location, Distance distance);
// annulus: between min and max distance
GeoResults<Restaurant> findByLocationNear(Point location, Range<Distance> range);
// inside a shape, unordered, no distance
List<Restaurant> findByLocationWithin(Circle area);
}
// usage
// Point p = new Point(-73.99, 40.73); // lng, lat
// Distance d = new Distance(2, Metrics.KILOMETERS); // spherical
// repo.findByLocationNear(p, d);go deeper
Know Near takes a Point + Distance and needs a geo index on the field.
Know GeoResults carries distances, Within uses shapes, and the index is mandatory.
Explain 2d vs 2dsphere, Metrics-driven spherical queries, Range for annulus, and GeoJSON coordinate order.
Reason about $geoNear-as-first-aggregation-stage, index selectivity, sharding constraints on geo indexes, and lat/long data quality at scale.
**Geospatial queries** find documents near a point or within a shape. Spring Data MongoDB exposes them through both derived query keywords and geo-specific types in `org.springframework.data.geo` and `org.springframework.data.mongodb.core.geo`. **The document + index:** the entity has a location field. You index it with **`@GeoSpatialIndexed`**, choosing a type via `type = GeoSpatialIndexType.GEO_2DSPHERE` (for real lat/long / GeoJSON, spherical geometry) or `GEO_2D` (flat plane, legacy coordinate pairs). **A geospatial index is mandatory** — MongoDB's `$near`/`$nearSphere` operators error without one. The field is typically a `GeoJsonPoint` (GeoJSON `{ type:'Point', coordinates:[lng, lat] }`) or a legacy `double[]`/`Point`. **Near keyword:** a derived method ``` GeoResults<Restaurant> findByLocationNear(Point location, Distance distance); ``` generates a proximity query returning documents **sorted by ascending distance** from `location`, limited to within `distance`. Key types: - **`Point(x, y)`** = the reference coordinate (longitude, latitude order for GeoJSON). - **`Distance(value, Metric)`** = the radius. Using **`Metrics.KILOMETERS`** or `MILES` marks it **spherical**, so Spring emits `$nearSphere`/`$geoNear` against a **2dsphere** index and converts to radians correctly. A `Distance` with no metric implies flat **2d** geometry. - **`Range<Distance>`** (min..max) can be passed for an annulus (near but not too near). **Within keyword:** `findByLocationWithin(Circle circle)` (or `Box`, `Polygon`, `GeoJsonPolygon`) generates a **`$geoWithin`** query returning all documents inside the shape — **unordered**, no distance computed. `Circle(Point center, Distance radius)`, `Box(Point first, Point second)`, `Polygon(Point...)` come from the geo package. **Return types:** - `List<T>` — just the matching documents. - **`GeoResults<T>`** — a collection of **`GeoResult<T>`**, each pairing the document with its **computed `Distance`** from the reference point. Only meaningful for `Near` (distance-producing) queries. - **`GeoPage<T>`** — paginated geo results. **@Query form:** you can also write a native geo filter, e.g. `@Query("{ 'location' : { $near : { $geometry : ?0, $maxDistance : ?1 } } }")`. **Gotchas:** - **Coordinate order** for GeoJSON is **[longitude, latitude]** — reversing them silently returns wrong/empty results. - Missing geospatial index -> query fails at runtime. - Mixing a plain `Distance` (2d) with a 2dsphere index or vice-versa gives wrong distances; keep metric ↔ index type consistent. - `$near` cannot be used inside some aggregation contexts; use `$geoNear` as the first pipeline stage there. - `Within` returns no distance — use `Near`/`GeoResults` when you need the distance value. - `$near`'s implicit sort means you generally don't add your own sort; `$maxDistance`/`$minDistance` bound the ring. **When to use:** store-locator, 'restaurants within 2 km', ride matching. Prefer **GeoJSON + 2dsphere + Metrics.KILOMETERS** for anything on the real Earth; legacy 2d only for simple flat-plane coordinates.
- Why does passing a Distance with Metrics.KILOMETERS change the generated query?A metric-bearing Distance marks the query as spherical, so Spring Data emits $nearSphere/$geoNear against a 2dsphere index and converts the radius to radians. A metric-less Distance implies flat 2d geometry — mixing them gives wrong distances.
- What's the difference between the Near and Within keywords?Near ($near) returns documents sorted by ascending distance up to a radius and can report each distance via GeoResults. Within ($geoWithin) returns all documents inside a shape (Circle/Box/Polygon) unordered, with no distance computed.
saying these in an interview costs you the question
- Forgetting the geospatial index — $near then fails at runtime
- Using [latitude, longitude] instead of GeoJSON's [longitude, latitude]
- Expecting a Within query to give you each document's distance