Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright 2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package example.springdata.mongodb.customer;

import lombok.Data;
import org.springframework.data.mongodb.core.geo.GeoJsonPolygon;
import org.springframework.data.mongodb.core.index.GeoSpatialIndexType;
import org.springframework.data.mongodb.core.index.GeoSpatialIndexed;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.util.Assert;

/**
* An entity to represent a Store with a service area.
*
* @author Rishabh Saraswat
*/
@Data
@Document
public class Store {

private String id;
private final String name;
@GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE)
private final GeoJsonPolygon serviceArea; // unlike Polygon, GeoJsonPolygon is closed boundary

/**
* Creates a new {@link Store} with the given name and service area.
*
* @param name must not be {@literal null} or empty.
* @param serviceArea must not be {@literal null}.
*/
public Store(String name, GeoJsonPolygon serviceArea) {
Assert.hasText(name, "Name must not be empty");
Assert.notNull(serviceArea, "Service area must not be null");
this.name = name;
this.serviceArea = serviceArea;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package example.springdata.mongodb.customer;

import org.springframework.data.repository.CrudRepository;

/**
* Repository interface for {@link Store} instances.
*
* @author Rishabh Saraswat
*/
interface StoreRepository extends CrudRepository<Store, String>{
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2014-2021 the original author or authors.
* Copyright 2014-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -15,43 +15,47 @@
*/
package example.springdata.mongodb.customer;

import static org.assertj.core.api.Assertions.*;
import static org.assertj.core.data.Offset.offset;

import example.springdata.mongodb.util.MongoContainers;

import java.util.stream.Stream;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
import org.springframework.data.domain.Limit;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Metrics;
import org.springframework.data.geo.Point;
import org.springframework.data.geo.Polygon;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.geo.GeoJsonPolygon;
import org.springframework.data.mongodb.core.index.GeospatialIndex;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.querydsl.QSort;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;

import org.testcontainers.containers.MongoDBContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

import java.util.List;
import java.util.stream.Stream;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.data.Offset.offset;

/**
* Integration test for {@link CustomerRepository}.
*
* @author Oliver Gierke
* @author Rishabh Saraswat
*/
@Testcontainers
@DataMongoTest
class CustomerRepositoryIntegrationTest {

@Container //
private static MongoDBContainer mongoDBContainer = MongoContainers.getDefaultContainer();
private static final MongoDBContainer mongoDBContainer = MongoContainers.getDefaultContainer();

@DynamicPropertySource
static void setProperties(DynamicPropertyRegistry registry) {
Expand All @@ -60,17 +64,23 @@ static void setProperties(DynamicPropertyRegistry registry) {

@Autowired CustomerRepository repository;
@Autowired MongoOperations operations;
@Autowired StoreRepository storeRepository;

private Customer dave, oliver, carter;
private Store store;

@BeforeEach
void setUp() {

repository.deleteAll();

GeoJsonPolygon polygon = new GeoJsonPolygon(new Point(0.0, 0.0), new Point(0.0, 1.0), new Point(1.0, 1.0), new Point(1.0, 0.0), new Point(0.0, 0.0));

dave = repository.save(new Customer("Dave", "Matthews"));
oliver = repository.save(new Customer("Oliver August", "Matthews"));
carter = repository.save(new Customer("Carter", "Beauford"));

store = storeRepository.save(new Store("store-1", polygon));
}

/**
Expand Down Expand Up @@ -146,4 +156,28 @@ void exposesGeoSpatialFunctionality() {
assertThat(distanceToFirstStore.getMetric()).isEqualTo(Metrics.KILOMETERS);
assertThat(distanceToFirstStore.getValue()).isCloseTo(0.862, offset(0.001));
}

/**
* Test case to show the usage of the geospatial operator {@code $geoIntersects}.
*/
@Test
void supportsGeoIntersectsPointInside() {
operations.indexOps(Store.class).createIndex(new GeospatialIndex("serviceArea"));
GeoJsonPoint pointInside = new GeoJsonPoint(0.5, 0.5);
Query pointInsideQuery = Query.query(Criteria.where("serviceArea").intersects(pointInside));

List<Store> stores = operations.find(pointInsideQuery, Store.class);
assertThat(stores).hasSize(1);
assertThat(stores.get(0).getName()).isEqualTo("store-1");
}

@Test
void supportsGeoIntersectsPointOutside() {
operations.indexOps(Store.class).createIndex(new GeospatialIndex("serviceArea"));
GeoJsonPoint pointOutside = new GeoJsonPoint(0.5, 0.5);
Query pointOutsideQuery = Query.query(Criteria.where("serviceArea").intersects(pointOutside));

List<Store> stores = operations.find(pointOutsideQuery, Store.class);
assertThat(stores).isEmpty();
}
}
Loading