使用Spring Data MongoDB進行地理位置相關(guān)查詢的步驟和示例
以下是如何使用 Spring Data MongoDB 進行地理位置相關(guān)查詢的步驟和示例:
核心概念:
- GeoJSON 對象: MongoDB 推薦使用 GeoJSON 格式來存儲地理位置數(shù)據(jù)。Spring Data MongoDB 提供了相應(yīng)的 GeoJSON 類型,如
GeoJsonPoint,GeoJsonPolygon,GeoJsonLineString等。GeoJsonPoint: 表示一個點,例如[longitude, latitude]。
- 地理空間索引 (Geospatial Index): 為了高效地執(zhí)行地理位置查詢,必須在存儲位置數(shù)據(jù)的字段上創(chuàng)建地理空間索引。
2dsphere: 支持球面幾何計算,適用于地球表面的經(jīng)緯度數(shù)據(jù)(推薦)。2d: 支持平面幾何計算,適用于二維平面上的點。
- 查詢操作符: MongoDB 提供了多種地理位置查詢操作符:
$near/$nearSphere: 查找靠近某個點的文檔,并按距離排序。$geoWithin: 查找?guī)缀涡螤睿ㄈ缍噙呅巍A形)內(nèi)的文檔。$geoIntersects: 查找與指定 GeoJSON 對象相交的文檔。$centerSphere(與$geoWithin結(jié)合使用): 定義一個球心和半徑的圓形區(qū)域進行查詢。
步驟詳解:
步驟 1: 添加依賴
確保你的 pom.xml (Maven) 或 build.gradle (Gradle) 文件中包含 Spring Data MongoDB 的依賴:
<!-- pom.xml (Maven) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
步驟 2: 定義實體 (Entity)
在你的實體類中,使用 org.springframework.data.mongodb.core.geo.GeoJsonPoint (或其他 GeoJSON 類型) 來存儲位置信息。
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.index.GeoSpatialIndexType;
import org.springframework.data.mongodb.core.index.GeoSpatialIndexed;
import org.springframework.data.mongodb.core.mapping.Document;
@Document(collection = "locations")
public class LocationEntity {
@Id
private String id;
private String name;
// 存儲經(jīng)緯度信息,并創(chuàng)建 2dsphere 索引
@GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE)
private GeoJsonPoint location; // [longitude, latitude]
public LocationEntity() {}
public LocationEntity(String name, GeoJsonPoint location) {
this.name = name;
this.location = location;
}
// Getters and Setters
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public GeoJsonPoint getLocation() {
return location;
}
public void setLocation(GeoJsonPoint location) {
this.location = location;
}
@Override
public String toString() {
return "LocationEntity{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", location=" + (location != null ? location.getCoordinates() : null) +
'}';
}
}
注意:
@GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE)注解會自動在location字段上創(chuàng)建2dsphere索引。這是進行地理位置查詢的關(guān)鍵。- GeoJSON 點的坐標(biāo)順序是
[longitude, latitude](經(jīng)度在前,緯度在后)。
步驟 3: 創(chuàng)建 Repository 接口
Spring Data MongoDB 可以通過方法名派生查詢,或者使用 @Query 注解自定義查詢。
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Point;
import org.springframework.data.geo.Polygon;
import org.springframework.data.mongodb.repository.MongoRepository;
import java.util.List;
public interface LocationRepository extends MongoRepository<LocationEntity, String> {
// 1. 查找靠近某個點的文檔 (使用 $nearSphere)
// Spring Data 會自動使用 $nearSphere 因為索引是 2dsphere
// Point 來自 org.springframework.data.geo.Point (x=longitude, y=latitude)
// Distance 來自 org.springframework.data.geo.Distance
List<LocationEntity> findByLocationNear(Point point, Distance distance);
// 也可以只按點查找,不限制距離 (結(jié)果按距離排序)
List<LocationEntity> findByLocationNear(Point point);
// 2. 查找在指定多邊形內(nèi)的文檔 (使用 $geoWithin)
// Polygon 來自 org.springframework.data.geo.Polygon
List<LocationEntity> findByLocationWithin(Polygon polygon);
// 3. 查找在指定圓形區(qū)域內(nèi)的文檔 (使用 $geoWithin 和 $centerSphere)
// Circle 來自 org.springframework.data.geo.Circle
// Spring Data 會將其轉(zhuǎn)換為 $geoWithin 與 $centerSphere
List<LocationEntity> findByLocationWithin(org.springframework.data.geo.Circle circle);
// 4. 查找與指定 GeoJSON 幾何圖形相交的文檔 (使用 $geoIntersects)
// 需要使用 MongoTemplate 或 @Query 來實現(xiàn)更復(fù)雜的 GeoJSON 相交查詢,
// 因為派生查詢對 $geoIntersects 的支持有限,尤其是對于復(fù)雜的 GeoJSON 輸入。
// 但簡單的 Point 相交可以。
// 對于更復(fù)雜的 GeoJSON (如 Polygon),通常使用 MongoTemplate 或 @Query
// List<LocationEntity> findByLocationIntersects(GeoJson geometry); // 示例,可能需要自定義實現(xiàn)
}
使用的 Spring Data Geo 類型:
org.springframework.data.geo.Point: 用于查詢參數(shù),表示一個點 (x 對應(yīng)經(jīng)度, y 對應(yīng)緯度)。org.springframework.data.geo.Distance: 用于指定距離,可以包含單位 (如Metrics.KILOMETERS)。org.springframework.data.geo.Polygon: 用于查詢參數(shù),表示一個多邊形。org.springframework.data.geo.Circle: 用于查詢參數(shù),表示一個圓形。org.springframework.data.geo.Box: 用于查詢參數(shù),表示一個矩形。
步驟 4: 使用 Repository 或 MongoTemplate 進行查詢
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.*;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.geo.GeoJsonPolygon;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import jakarta.annotation.PostConstruct;
import java.util.Arrays;
import java.util.List;
@Service
public class LocationService {
@Autowired
private LocationRepository locationRepository;
@Autowired
private MongoTemplate mongoTemplate;
@PostConstruct
public void init() {
locationRepository.deleteAll(); // 清理舊數(shù)據(jù)
// 插入一些示例數(shù)據(jù)
// 故宮 (116.403963, 39.915119)
locationRepository.save(new LocationEntity("Forbidden City", new GeoJsonPoint(116.403963, 39.915119)));
// 天安門廣場 (116.3912757, 39.9037078)
locationRepository.save(new LocationEntity("Tiananmen Square", new GeoJsonPoint(116.3912757, 39.9037078)));
// 頤和園 (116.275136, 39.999077)
locationRepository.save(new LocationEntity("Summer Palace", new GeoJsonPoint(116.275136, 39.999077)));
// 東方明珠 (121.499718, 31.239703)
locationRepository.save(new LocationEntity("Oriental Pearl Tower", new GeoJsonPoint(121.499718, 31.239703)));
}
public void performGeoQueries() {
System.out.println("--- Performing Geo Queries ---");
// 中心點: 北京市中心附近 (例如王府井 116.417427, 39.913904)
Point centerPoint = new Point(116.417427, 39.913904); // longitude, latitude
// 1. 查找王府井附近 5 公里內(nèi)的地點
Distance fiveKilometers = new Distance(5, Metrics.KILOMETERS);
List<LocationEntity> nearWangfujing = locationRepository.findByLocationNear(centerPoint, fiveKilometers);
System.out.println("\nLocations near Wangfujing (5km):");
nearWangfujing.forEach(System.out::println); // 應(yīng)該包含故宮和天安門
// 2. 查找在指定多邊形內(nèi)的地點 (大致覆蓋北京二環(huán)內(nèi))
// 注意:多邊形的點必須形成閉合環(huán)路,且第一個點和最后一個點相同
Polygon beijingRing2 = new Polygon(
new Point(116.30, 39.85), //西南
new Point(116.50, 39.85), //東南
new Point(116.50, 39.95), //東北
new Point(116.30, 39.95), //西北
new Point(116.30, 39.85) //閉合
);
List<LocationEntity> withinBeijingRing2 = locationRepository.findByLocationWithin(beijingRing2);
System.out.println("\nLocations within Beijing Ring 2 (approx):");
withinBeijingRing2.forEach(System.out::println); // 應(yīng)該包含故宮和天安門
// 3. 查找在指定圓形區(qū)域內(nèi)的地點 (以故宮為圓心,2公里為半徑)
Point forbiddenCityCoords = new Point(116.403963, 39.915119);
Distance twoKilometers = new Distance(2, Metrics.KILOMETERS);
// 對于2dsphere索引, Circle的距離單位會被正確處理 (例如轉(zhuǎn)換為弧度)
Circle aroundForbiddenCity = new Circle(forbiddenCityCoords, twoKilometers);
List<LocationEntity> withinCircle = locationRepository.findByLocationWithin(aroundForbiddenCity);
System.out.println("\nLocations within 2km of Forbidden City:");
withinCircle.forEach(System.out::println); // 應(yīng)該包含故宮和天安門
// 4. 使用 MongoTemplate 進行 $geoIntersects 查詢
// 定義一個 GeoJsonPolygon (注意點順序,逆時針為外部,順時針為內(nèi)部,但通常簡單多邊形即可)
// 這里用和上面一樣的多邊形,但用 GeoJsonPolygon
GeoJsonPolygon queryPolygon = new GeoJsonPolygon(
new Point(116.30, 39.85),
new Point(116.50, 39.85),
new Point(116.50, 39.95),
new Point(116.30, 39.95),
new Point(116.30, 39.85)
);
Query intersectsQuery = new Query(Criteria.where("location").intersects(queryPolygon));
List<LocationEntity> intersectingLocations = mongoTemplate.find(intersectsQuery, LocationEntity.class);
System.out.println("\nLocations intersecting with query polygon (MongoTemplate):");
intersectingLocations.forEach(System.out::println);
// 5. 使用 MongoTemplate 進行 $nearSphere 查詢,并指定最小和最大距離
Query nearQueryWithMinMax = new Query(
Criteria.where("location")
.nearSphere(centerPoint) // 使用 Spring Data Point
.minDistance(1000 / 6378137.0) // 最小距離1公里 (轉(zhuǎn)換為弧度,MongoDB $nearSphere 需要弧度或米)
// 或者直接用米: .minDistance(1000) 如果MongoDB版本支持
.maxDistance(5000 / 6378137.0) // 最大距離5公里
// 或者直接用米: .maxDistance(5000)
);
// 如果MongoDB 4.0+ 且 Spring Data MongoDB 2.2+, 可以直接用米
// Query nearQueryWithMinMaxMeters = new Query(
// Criteria.where("location")
// .nearSphere(centerPoint)
// .minDistance(1000.0) // 1000 meters
// .maxDistance(5000.0) // 5000 meters
// );
// List<LocationEntity> nearWithMinMax = mongoTemplate.find(nearQueryWithMinMaxMeters, LocationEntity.class);
// System.out.println("\nLocations near Wangfujing (1km-5km, MongoTemplate):");
// nearWithMinMax.forEach(System.out::println);
// 對于 $nearSphere,Spring Data 的 Repository 方法中的 Distance 對象會自動處理單位轉(zhuǎn)換。
// 使用 MongoTemplate 時,對于 $minDistance / $maxDistance:
// - 如果是 `2dsphere` 索引,MongoDB 期望距離單位是米。
// - 如果是 `2d` 索引,MongoDB 期望距離單位是索引坐標(biāo)系的單位。
// Spring Data MongoDB 3.0+ 配合 MongoDB 4.0+,`nearSphere` 可以直接接受米為單位的 `minDistance`/`maxDistance`。
// 如果使用較舊版本,可能需要將距離轉(zhuǎn)換為弧度(如示例中除以地球半徑)。
// 簡單的 findByLocationNear(Point, Distance) 通常是更方便的選擇。
}
}
運行示例 (在一個 Spring Boot 應(yīng)用中):
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class MongoGeoApplication {
public static void main(String[] args) {
SpringApplication.run(MongoGeoApplication.class, args);
}
@Bean
CommandLineRunner runner(LocationService locationService) {
return args -> {
locationService.performGeoQueries();
};
}
}
總結(jié)與要點:
- 實體定義: 使用
GeoJsonPoint(或其他GeoJson*類型) 存儲位置,并用@GeoSpatialIndexed創(chuàng)建2dsphere索引。 - 坐標(biāo)順序: 始終記住 GeoJSON 使用
[longitude, latitude]。Spring Data 的Point對象構(gòu)造函數(shù)new Point(x, y)中x是經(jīng)度,y是緯度。 - Repository 查詢: Spring Data Repositories 為常見的地理位置查詢(如
Near,Within)提供了便捷的方法名派生。 MongoTemplate: 對于更復(fù)雜或自定義的地理位置查詢(如$geoIntersects配合復(fù)雜 GeoJSON 對象,或需要更精細控制$nearSphere的$minDistance/$maxDistance),可以使用MongoTemplate。- 單位:
org.springframework.data.geo.Distance: 允許你指定單位 (如Metrics.KILOMETERS,Metrics.MILES)。Spring Data 會在與 MongoDB 交互時處理轉(zhuǎn)換。- MongoDB 的
$nearSphere和$centerSphere(用于2dsphere索引) 默認(rèn)使用米作為距離單位。 - 當(dāng)使用
MongoTemplate時,需要注意minDistance/maxDistance的單位,較新版本的 MongoDB (4.0+) 和 Spring Data MongoDB (2.2+/3.0+) 可以直接使用米。
- 性能: 地理空間索引對于查詢性能至關(guān)重要。確保索引已正確創(chuàng)建。
以上就是使用Spring Data MongoDB進行地理位置相關(guān)查詢的步驟和示例的詳細內(nèi)容,更多關(guān)于Spring Data MongoDB地理位置查詢的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java 8對LinkedHashSet元素進行排序的操作方法
LinkedHashSet 是 Java 集合框架中的一個類,它繼承自 HashSet,并實現(xiàn)了 Set 接口,然而,LinkedHashSet 不支持元素的排序,它僅僅保持插入順序,所以本文給大家介紹了Java 8 如何對 LinkedHashSet 元素進行排序,需要的朋友可以參考下2024-11-11
Spring配置動態(tài)數(shù)據(jù)源實現(xiàn)讀寫分離的方法
這篇文章主要介紹了利用Spring配置動態(tài)數(shù)據(jù)源實現(xiàn)讀寫分離的方法,文中通過示例代碼介紹的很詳細,相信對大家的理解和學(xué)習(xí)具有一定的參考借鑒價值,藕需要的朋友可以一起學(xué)習(xí)學(xué)習(xí)。2017-01-01
rabbitmq的消息持久化處理開啟,再關(guān)閉后,消費者啟動報錯問題
這篇文章主要介紹了rabbitmq的消息持久化處理開啟,再關(guān)閉后,消費者啟動報錯問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-11-11
MybatisPlus更新為null的字段及自定義sql注入
mybatis-plus在執(zhí)行更新操作,當(dāng)更新字段為空字符串或者null的則不會執(zhí)行更新,本文主要介紹了MybatisPlus更新為null的字段及自定義sql注入,感興趣的可以了解一下2024-05-05
SpringBoot+ThreadLocal+AbstractRoutingDataSource實現(xiàn)動態(tài)切換數(shù)據(jù)源
最近在做業(yè)務(wù)需求時,需要從不同的數(shù)據(jù)庫中獲取數(shù)據(jù)然后寫入到當(dāng)前數(shù)據(jù)庫中,因此涉及到切換數(shù)據(jù)源問題,所以本文采用ThreadLocal+AbstractRoutingDataSource來模擬實現(xiàn)dynamic-datasource-spring-boot-starter中線程數(shù)據(jù)源切換,需要的朋友可以參考下2023-08-08
SpringBoot3和ShardingSphere5框架實現(xiàn)數(shù)據(jù)分庫分表
這篇文章主要介紹了SpringBoot3和ShardingSphere5框架實現(xiàn)數(shù)據(jù)分庫分表的相關(guān)資料,需要的朋友可以參考下2023-08-08
Springboot項目參數(shù)校驗方式(Validator)
本文介紹了如何在Spring Boot項目中使用`spring-boot-starter-validation`包和注解來實現(xiàn)請求參數(shù)校驗,主要介紹了校驗注解的使用方法、校驗失敗的異常捕獲以及`@Validated`的分組功能2025-02-02

