Spring Data MongoDB 教程

Criteria 地理空间查询

MongoDB 支持空间数据的存储,不过限制了类型必须为 GeoJSON。

MongoDB 可以满足一些空间查询的需求,对于不需要空间数据处理的项目极为方便,不需要第三方库来充当空间数据的操作层。

MongoDB 可以为 GeoJSON 类型数据建立索引,提升空间查询的效率。

MongoDB 为了地理空间查询定义了如下操作符:

  • $geoIntersects 选择地理空间数据与指定的 GeoJSON 对象相交的文档,即数据和指定对象的交集为非空的文档。2dsphere 索引支持 $geoIntersects 操作符。$geoIntersects 使用 $geometry 操作符定义 GeoJSON 对象。例如:

// 查询与多边形相交的文档
db.places.find({
   loc: {
       $geoIntersects: {
           $geometry: {
               type: "Polygon",
               coordinates:[
                   [ [0,0],[3,6],[6,1],[0,0]  ]
               ]
           }
       }
   }
})
  • $geoWithin 选择具有完全存在于指定形状内的地理空间数据的文档,2dsphere 和 2d 索引都支持 $geoWithin。$geoWithin 运用 $geometry 操作符指定 GeoJSON对象。例如:

// 查询完全存在于 GeoJSON 多边形内的所有 loc 数据
db.places.find({
   loc: {
       $geoWithin: {
           $geometry: {
               type: "Polygon",
               coordinates: [
                   [  [0,0],[3,6],[6,1],[0,0] ]
               ]
           }
       }
   }
})
  • $near 返回接近点的地理空间对象,需要地理空间索引。2dsphere 和 2d 索引支持 $near。例如:

// 查询离指定的 GeoJson 点至少 1000 米的文档
db.places.find({
   location:{
       $near: {
           $geometry: {
               type:"Point",
               coordinates:[-73.9667,40.78]
           },
           $minDistance:1000,
           $maxDistance:5000
       }
   }
})
  • $nearSphere 返回接近球面点上的地理空间对象,2dsphere 和 2d 索引支持 $nearSphere。例如:

// 查询离指定点至少 1000 米,至多 5000 米的位置
db.places.find({
   location:{
       $nearSphere: {
           $geometry: {
               type:"Point",
               coordinates:[-73.9667,40.78]
           },
           $minDistance:1000,
           $maxDistance:5000
       }
   }
})
  • $maxDistance 操作符将地理空间 $near 或 $nearSphere 查询的结果约束为指定的距离。最大距离的测量单位由使用中的坐标系确定。对于 Geo JSON 点对象,以米为单位指定距离,而不是以弧度为单位。必须为指定非负数 $maxDistance。例如:

db.places.find( {
  loc: { $near: [ -74 , 40 ],  $maxDistance: 10 }
} )
  • $minDistance 将地理空间的 $near 或 $nearSphere 查询的结果过滤为与中心点至少有指定距离的文件。例如:

db.places.find(
  {
    location:
      { $near :
         {
           $geometry: { type: "Point",  coordinates: [ -73.9667, 40.78 ] },
           $minDistance: 1000,
           $maxDistance: 5000
         }
      }
  }
)

下面将介绍怎样使用 Criteria 实现地理空间查询,Criteria 类定义的地理空间查询方法如下:

  • Criteria intersects(GeoJson geoJson) 使用 $geoIntersects 操作符创建 Criteria,该运算符匹配给定 geoJson 结构和文档结构的交集。

  • Criteria within(Shape shape) 使用 $geoWithin 操作符创建地理空间查询条件。

  • Criteria withinSphere(Circle circle) 使用 $geoWithin、$centerSphere 操作符创建地理空间查询条件。

  • Criteria near(Point point) 使用 $near 操作符创建地理空间查询条件。

  • Criteria nearSphere(Point point) 使用 $nearSphere 操作符创建地理空间查询条件。

  • Criteria maxDistance(double maxDistance) 使用 $maxDistance 操作符创建一个地理空间 Criteria,供 $near 或 $nearSphere 操作符使用。

  • Criteria minDistance(double minDistance) 使用 $minDistance 操作符创建一个地理空间 Criteria,供 $near 或 $nearSphere 操作符使用。

示例

下面示例将演示怎样使用 Criteria 的地理空间查询方法实现地理空间查询,示例如下:

package com.hxstrive.springdata.mongodb;

import com.hxstrive.springdata.mongodb.entity.Person;
import com.mongodb.client.model.IndexOptions;
import org.bson.Document;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.core.MongoTemplate;
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 java.util.List;

/**
* 地理位置查询
* @author hxstrive.com
*/
@SpringBootTest
class CriteriaDemo5 {

   @Autowired
   private MongoTemplate mongoTemplate;

   @BeforeEach
   public void init() {
       // 删除集合
       mongoTemplate.dropCollection(Person.class);

       // 准备数据
       mongoTemplate.insert(Person.builder().id(100).name("Tom").age(27).email("Tom@sina.com")
               .coordinates(new Double[]{ 116.424966, 39.936625 }).build());
       mongoTemplate.insert(Person.builder().id(200).name("Helen").age(30).email("Helen@outlook.com")
               .coordinates(new Double[]{ 114.530044, 38.056057 }).build());
       mongoTemplate.insert(Person.builder().id(300).name("Bill").age(47).email("bill@gmail.com")
               .coordinates(new Double[]{ 114.530044, 38.056057 }).build());
       mongoTemplate.insert(Person.builder().id(400).name("Joe").age(20).email("joe@163.com")
               .coordinates(new Double[]{ 114.364468, 30.650303 })
               .summary("User description information").build());

       // 创建索引
       String collectionName = mongoTemplate.getCollectionName(Person.class);
       mongoTemplate.getCollection(collectionName).createIndex(
               // 为 coordinates 字段创建 2d 类型的索引
               new Document("coordinates", "2d"),
               // 设置索引名为 idx_coordinates,background 指定索引是否应该在后台创建
               new IndexOptions().background(false).name("idx_coordinates"));
   }

   @Test
   public void intersects() {
       // 定义一个多边形
       GeoJsonPolygon geoJson = new GeoJsonPolygon(
               new Point(116.233519, 40.340382),
               new Point(111.845764, 37.984649),
               new Point(111.845764, 37.984649),
               new Point(119.811793, 39.054212),
               new Point(116.233519, 40.340382)
       );

       // 查询 coordinates 文档经纬度与多边形有交集的文档
       Criteria criteria = Criteria.where("coordinates").intersects(geoJson);

       List<Person> personList = mongoTemplate.query(Person.class)
               .matching(Query.query(criteria)).all();
       for(Person person : personList) {
           System.out.println(person);
       }
       // 结果:
       // Person(id=100, name=Tom, age=27, email=Tom@sina.com, summary=null, coordinates=[116.424966, 39.936625])
   }

   @Test
   public void within() {
       // 定义一个多边形
       GeoJsonPolygon geoJson = new GeoJsonPolygon(
               new Point(116.233519, 40.340382),
               new Point(111.845764, 37.984649),
               new Point(111.845764, 37.984649),
               new Point(119.811793, 39.054212),
               new Point(116.233519, 40.340382)
       );

       // 查询 coordinates 文档经纬度与多边形有交集的文档
       Criteria criteria = Criteria.where("coordinates").within(geoJson);

       List<Person> personList = mongoTemplate.query(Person.class)
               .matching(Query.query(criteria)).all();
       for(Person person : personList) {
           System.out.println(person);
       }
       // 结果:
       // Person(id=100, name=Tom, age=27, email=Tom@sina.com, summary=null, coordinates=[116.424966, 39.936625])
   }

   @Test
   public void near() {
       // 注意,查询之前需要给经纬度字段创建索引信息
       // 查询经纬度 116.424966,39.936625 附近的文档
       Criteria criteria = Criteria.where("coordinates")
               .near(new Point(new Point(116.424966, 39.936625)))
               // 单位:弧度
               .minDistance(0.1)
               .maxDistance(5.0);

       List<Person> personList = mongoTemplate.query(Person.class)
               .matching(Query.query(criteria)).all();
       for(Person person : personList) {
           System.out.println(person);
       }
       // 结果:
       // Person(id=200, name=Helen, age=30, email=Helen@outlook.com, summary=null, coordinates=[114.530044, 38.056057])
       // Person(id=300, name=Bill, age=47, email=bill@gmail.com, summary=null, coordinates=[114.530044, 38.056057])
   }

}

其中,Person 实体代码如下:

package com.hxstrive.springdata.mongodb.entity;

import lombok.Builder;
import lombok.Data;
import lombok.ToString;

@Data
@ToString
@Builder
public class Person {
   /** 用户ID */
   private int id;
   /** 用户姓名 */
   private String name;
   /** 年龄 */
   private int age;
   /** 电子邮件 */
   private String email;
   /** 个人说明 */
   private String summary;
   /** 经纬度信息 */
   private Double[] coordinates;
}
说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号