Spatial Queries

What You Will Learn

How to define geospatial entities with @GeoPoint and @GeoField annotations, create indexed caches, and run spatial Ickle queries including within circle, within box, within polygon, distance projections, and spatial ordering.

Prerequisites

  • Java 17+

  • An Infinispan Server running on localhost:11222 (or Docker/Podman available for Testcontainers)

Start an Infinispan Server with Docker or Podman:

docker run -it --rm -p 11222:11222 -e USER=admin -e PASS=password quay.io/infinispan/server:latest
Tip
You can replace docker with podman in the command above if you use Podman.
Tip
If no server is running, the tutorial code automatically starts an Infinispan Server using Testcontainers.

Step 1: Define Geospatial Entities

Use @GeoPoint with @Latitude and @Longitude to define a location on a record:

@Proto
@Indexed
@GeoPoint(fieldName = "location", projectable = true, sortable = true)
public record Restaurant(
      @Keyword(normalizer = "lowercase", projectable = true, sortable = true) String name,
      @Text String description,
      @Text String address,
      @Latitude(fieldName = "location") Double latitude,
      @Longitude(fieldName = "location") Double longitude,
      @Basic Float score
) {
}

For entities with multiple geo points, use multiple @GeoPoint annotations (see TrainRoute). Alternatively, use @GeoField with LatLng for a simpler mapping:

@Proto
@Indexed
public record Hiking(@Keyword String name, @GeoField LatLng start, @GeoField LatLng end) {

}

Step 2: Create an Indexed Cache and Run Spatial Queries

Configure the cache with indexing enabled for all spatial entities. Then run spatial queries — within a circle, within a bounding box, within a polygon, and with distance projections and ordering:

   static QueryResult<Restaurant> withinCircle() {
      Query<Restaurant> query = myCache.query("from tutorial.Restaurant r where r.location " +
            "within circle(:lat, :lon, :distance)");
      query.setParameter("lat", MY_COORDINATES.latitude());
      query.setParameter("lon", MY_COORDINATES.longitude());
      query.setParameter("distance", 100);
      QueryResult<Restaurant> queryResult = query.execute();
      // Print the results
      System.out.println("COUNT " + queryResult.count().value());
      System.out.println(queryResult.list());
      return queryResult;
   }

   static QueryResult<Restaurant> withinBox() {
      Query<Restaurant> query = myCache.query("from tutorial.Restaurant r where r.location " +
            "within box(41.91, 12.45, 41.90, 12.46)");
      QueryResult<Restaurant> queryResult = query.execute();
      // Print the results
      System.out.println("COUNT " + queryResult.count().value());
      System.out.println(queryResult.list());
      return queryResult;
   }

   static QueryResult<Restaurant> withinPolygon() {
      Query<Restaurant> query = myCache.query("from tutorial.Restaurant r where r.location " +
            "within polygon((41.91, 12.45), (41.91, 12.46), (41.90, 12.46), (41.90, 12.46))");
      QueryResult<Restaurant> queryResult = query.execute();
      // Print the results
      System.out.println("COUNT " + queryResult.count().value());
      System.out.println(queryResult.list());
      return queryResult;
   }

   static List<RestaurantDTO> spatialProjection() {
      Query<Object[]> query = myCache.query("select r.name, distance(r.location, 41.90847031512531, 12.455633288333539) " +
            "from tutorial.Restaurant r");
      QueryResult<Object[]> queryResult = query.execute();
      List<RestaurantDTO> valueObjects = queryResult.list().stream()
            .map(r -> new RestaurantDTO((String) r[0], (Double) r[1]))
            .toList();
      // Print the results
      System.out.println("COUNT " + queryResult.count().value());
      System.out.println(valueObjects);
      return valueObjects;
   }

   static QueryResult<Restaurant> spatialOrderBy() {
      Query<Restaurant> query = myCache.query("from tutorial.Restaurant r where r.location " +
            "order by distance(r.location, 41.90847031512531, 12.455633288333539)");
      QueryResult<Restaurant> queryResult = query.execute();
      // Print the results
      System.out.println("COUNT " + queryResult.count().value());
      System.out.println(queryResult.list());
      return queryResult;
   }

   static List<RestaurantDTO> spatialProjection_OrderBy() {
      Query<Object[]> query = myCache.query("select r.name, distance(r.location, 41.90847031512531, 12.455633288333539) " +
            "from tutorial.Restaurant r where r.location " +
            "order by distance(r.location, 41.90847031512531, 12.455633288333539)");
      QueryResult<Object[]> queryResult = query.execute();
      List<RestaurantDTO> valueObjects = queryResult.list().stream()
            .map(r -> new RestaurantDTO((String) r[0], (Double) r[1]))
            .toList();
      // Print the results
      System.out.println("COUNT " + queryResult.count().value());
      System.out.println(valueObjects);
      return valueObjects;
   }

Step 3: Run the Tutorial

mvn package exec:java

What’s Next