What Morphium Offers

Morphium supports 2dsphere indexes, $near queries and GeoJSON natively through annotations and the fluent Query API. Store coordinates as double[] arrays, add @Index("2dsphere:location"), and query by proximity or bounding geometry — no manual index creation or coordinate conversion required.

The Challenge

Geospatial queries require specialized indexes, GeoJSON coordinate conventions (longitude first!) and specific query operators. Getting the index type or coordinate order wrong produces empty results with no error.

Morphium Features Used

GeoJSON Point [lng, lat] MongoDB uses GeoJSON convention: coordinates are stored as [longitude, latitude] (NOT lat/lng!). Morphium maps a Java double[] directly to a BSON array. Example: new double[]{11.576, 48.137} for Munich. MongoDBAtlasCosmosDB 2dsphere Index MongoDB index type for spherical (Earth-like) geometry. Required for geospatial query operators like $nearSphere, $geoWithin, and $geoIntersects. Created via db.stores.createIndex({location: "2dsphere"}) or Morphium's ensureIndicesFor(). MongoDBAtlasCosmosDB $near Query MongoDB geospatial operator that finds documents near a given point, sorted by distance. Requires a 2dsphere index. Supports $maxDistance in meters for radius limiting. Can be executed via Morphium's driver-level command API. MongoDBAtlasCosmosDB $geoWithin MongoDB geospatial operator that finds documents within a specified geographic shape (circle, polygon, box). Unlike $near, it does NOT sort by distance. Also requires a 2dsphere index. MongoDBAtlasCosmosDB @Entity Maps the Store class to the "stores" MongoDB collection. Required on every Morphium-managed entity. Import: de.caluga.morphium.annotations.Entity MongoDBAtlasCosmosDB @Index Creates a MongoDB index on the annotated field. On Store.name, it creates a single-field index for fast name lookups. Import: de.caluga.morphium.annotations.Index MongoDBAtlasCosmosDB ensureIndicesFor() Scans the entity class for @Index annotations and creates the corresponding MongoDB indexes if they do not exist. Idempotent and safe to call multiple times. Called in @PostConstruct to ensure indexes are ready before queries. Usage: morphium.ensureIndicesFor(Store.class) MongoDBAtlasCosmosDB

Prerequisites & Key Concepts

  • GeoJSON format is [longitude, latitude] — this is the opposite of the everyday "latitude, longitude" convention. Getting the order wrong is the most common mistake when working with geospatial data in MongoDB. Munich is [11.576, 48.137] (longitude first).
  • 2dsphere index required for geo queries — without this index, MongoDB cannot execute $nearSphere, $geoWithin, or $geoIntersects queries. The index can be created via the MongoDB shell or programmatically.
  • ensureIndicesFor() creates indexes from annotations — call this at startup (e.g., in @PostConstruct) to ensure all @Index-annotated fields have their corresponding MongoDB indexes. This is idempotent and safe to call multiple times.
  • Morphium maps double[] to BSON arrays — no special annotation needed. Java arrays and List<String> are automatically serialized to and from BSON arrays.

Entity Source Code

Store.java Java
import de.caluga.morphium.annotations.Entity;
import de.caluga.morphium.annotations.Id;
import de.caluga.morphium.annotations.Index;
import de.caluga.morphium.driver.MorphiumId;
import lombok.experimental.FieldNameConstants;

@Entity(collectionName = "stores")1
@Data @NoArgsConstructor @AllArgsConstructor @Builder
@FieldNameConstants
public class Store {

    @Id
    private MorphiumId id;

    @Index // single-field index for name lookups2
    private String name;

    private String address;
    private String city;
    private String country;

    // GeoJSON: [longitude, latitude] -- NOT [lat, lng]!
    private double[] location;3

    private String phone;

    // List<String> maps to BSON array automatically
    private List<String> services;4
}
1 @Entity maps this class to the stores MongoDB collection — required on every Morphium-managed entity.
2 @Index on a field creates a single-field index; ensureIndicesFor(Store.class) at startup materialises it in MongoDB.
3 A plain Java double[] stores the GeoJSON coordinate pair [longitude, latitude] — Morphium maps it directly to a BSON array, no special annotation required.
4 List<String> is also serialised to a BSON array automatically — Morphium handles all Java collection and array types natively.

Service Code

Morphium API — Geo Queries Java
import de.caluga.morphium.Morphium;

@Inject Morphium morphium;

// Ensure indexes at startup (reads @Index annotations)
@PostConstruct1
void init() {
    morphium.ensureIndicesFor(Store.class);2
}

// Store with coordinates [lng, lat]
Store store = Store.builder()
    .name("Munich Store")
    .address("Marienplatz 1")
    .city("Munich").country("Germany")
    .location(new double[]{11.576, 48.137}) // [lng, lat]3
    .phone("+49 89 1234567")
    .services(List.of("Repair", "Sales"))
    .build();
morphium.store(store);

// Nearby query pattern (driver-level command)
// Requires 2dsphere index on location field:
// db.stores.createIndex({location: "2dsphere"})
morphium.getDriver().runCommand("stores", Map.of(4
    "find", "stores",
    "filter", Map.of("location", Map.of(
        "$nearSphere", Map.of(5
            "$geometry", Map.of(
                "type", "Point",
                "coordinates", List.of(lng, lat)),
            "$maxDistance", maxDistMeters6
    ))))
));
1 @PostConstruct ensures index creation runs once at bean initialisation, before any geo queries are executed.
2 ensureIndicesFor(Store.class) reads all @Index annotations on the entity and creates the corresponding MongoDB indexes if they do not yet exist — idempotent and safe to call on every startup.
3 Coordinates must be [longitude, latitude] — the GeoJSON convention is the reverse of everyday "lat, lng" notation; swapping them causes silent wrong results.
4 Geospatial queries use Morphium's driver-level runCommand() to pass raw MongoDB commands when the query builder does not yet expose a geo-specific DSL.
5 $nearSphere finds documents sorted by spherical (great-circle) distance from the given point — requires a 2dsphere index on the location field.
6 $maxDistance limits results to documents within the given radius in metres, avoiding a full collection scan of distant stores.

Related Documentation