以下是如何使用 spring data mongodb 进行地理位置相关查询的步骤和示例:
核心概念:
- geojson 对象: mongodb 推荐使用 geojson 格式来存储地理位置数据。spring data mongodb 提供了相应的 geojson 类型,如
geojsonpoint,geojsonpolygon,geojsonlinestring等。geojsonpoint: 表示一个点,例如[longitude, latitude]。
- 地理空间索引 (geospatial index): 为了高效地执行地理位置查询,必须在存储位置数据的字段上创建地理空间索引。
2dsphere: 支持球面几何计算,适用于地球表面的经纬度数据(推荐)。2d: 支持平面几何计算,适用于二维平面上的点。
- 查询操作符: mongodb 提供了多种地理位置查询操作符:
$near/$nearsphere: 查找靠近某个点的文档,并按距离排序。$geowithin: 查找几何形状(如多边形、圆形)内的文档。$geointersects: 查找与指定 geojson 对象相交的文档。$centersphere(与$geowithin结合使用): 定义一个球心和半径的圆形区域进行查询。
步骤详解:
步骤 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;
// 存储经纬度信息,并创建 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字段上创建2dsphere索引。这是进行地理位置查询的关键。- geojson 点的坐标顺序是
[longitude, latitude](经度在前,纬度在后)。
步骤 3: 创建 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);
// 也可以只按点查找,不限制距离 (结果按距离排序)
list<locationentity> findbylocationnear(point point);
// 2. 查找在指定多边形内的文档 (使用 $geowithin)
// polygon 来自 org.springframework.data.geo.polygon
list<locationentity> findbylocationwithin(polygon polygon);
// 3. 查找在指定圆形区域内的文档 (使用 $geowithin 和 $centersphere)
// circle 来自 org.springframework.data.geo.circle
// spring data 会将其转换为 $geowithin 与 $centersphere
list<locationentity> findbylocationwithin(org.springframework.data.geo.circle circle);
// 4. 查找与指定 geojson 几何图形相交的文档 (使用 $geointersects)
// 需要使用 mongotemplate 或 @query 来实现更复杂的 geojson 相交查询,
// 因为派生查询对 $geointersects 的支持有限,尤其是对于复杂的 geojson 输入。
// 但简单的 point 相交可以。
// 对于更复杂的 geojson (如 polygon),通常使用 mongotemplate 或 @query
// list<locationentity> findbylocationintersects(geojson geometry); // 示例,可能需要自定义实现
}
使用的 spring data geo 类型:
org.springframework.data.geo.point: 用于查询参数,表示一个点 (x 对应经度, y 对应纬度)。org.springframework.data.geo.distance: 用于指定距离,可以包含单位 (如metrics.kilometers)。org.springframework.data.geo.polygon: 用于查询参数,表示一个多边形。org.springframework.data.geo.circle: 用于查询参数,表示一个圆形。org.springframework.data.geo.box: 用于查询参数,表示一个矩形。
步骤 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(); // 清理旧数据
// 插入一些示例数据
// 故宫 (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 公里内的地点
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); // 应该包含故宫和天安门
// 2. 查找在指定多边形内的地点 (大致覆盖北京二环内)
// 注意:多边形的点必须形成闭合环路,且第一个点和最后一个点相同
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); // 应该包含故宫和天安门
// 3. 查找在指定圆形区域内的地点 (以故宫为圆心,2公里为半径)
point forbiddencitycoords = new point(116.403963, 39.915119);
distance twokilometers = new distance(2, metrics.kilometers);
// 对于2dsphere索引, circle的距离单位会被正确处理 (例如转换为弧度)
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); // 应该包含故宫和天安门
// 4. 使用 mongotemplate 进行 $geointersects 查询
// 定义一个 geojsonpolygon (注意点顺序,逆时针为外部,顺时针为内部,但通常简单多边形即可)
// 这里用和上面一样的多边形,但用 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公里 (转换为弧度,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 对象会自动处理单位转换。
// 使用 mongotemplate 时,对于 $mindistance / $maxdistance:
// - 如果是 `2dsphere` 索引,mongodb 期望距离单位是米。
// - 如果是 `2d` 索引,mongodb 期望距离单位是索引坐标系的单位。
// spring data mongodb 3.0+ 配合 mongodb 4.0+,`nearsphere` 可以直接接受米为单位的 `mindistance`/`maxdistance`。
// 如果使用较旧版本,可能需要将距离转换为弧度(如示例中除以地球半径)。
// 简单的 findbylocationnear(point, distance) 通常是更方便的选择。
}
}
运行示例 (在一个 spring boot 应用中):
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();
};
}
}
总结与要点:
- 实体定义: 使用
geojsonpoint(或其他geojson*类型) 存储位置,并用@geospatialindexed创建2dsphere索引。 - 坐标顺序: 始终记住 geojson 使用
[longitude, latitude]。spring data 的point对象构造函数new point(x, y)中x是经度,y是纬度。 - repository 查询: spring data repositories 为常见的地理位置查询(如
near,within)提供了便捷的方法名派生。 mongotemplate: 对于更复杂或自定义的地理位置查询(如$geointersects配合复杂 geojson 对象,或需要更精细控制$nearsphere的$mindistance/$maxdistance),可以使用mongotemplate。- 单位:
org.springframework.data.geo.distance: 允许你指定单位 (如metrics.kilometers,metrics.miles)。spring data 会在与 mongodb 交互时处理转换。- mongodb 的
$nearsphere和$centersphere(用于2dsphere索引) 默认使用米作为距离单位。 - 当使用
mongotemplate时,需要注意mindistance/maxdistance的单位,较新版本的 mongodb (4.0+) 和 spring data mongodb (2.2+/3.0+) 可以直接使用米。
- 性能: 地理空间索引对于查询性能至关重要。确保索引已正确创建。
以上就是使用spring data mongodb进行地理位置相关查询的步骤和示例的详细内容,更多关于spring data mongodb地理位置查询的资料请关注代码网其它相关文章!
发表评论