Spring data mongodb实现LBS

基本介绍

位置服务(LBS)解决的主要问题是当前位置周围某个范围内的人或场所.
在传统的解决方案,开发人员须要根据复杂的几何运算与大量的SQL语句进行查找,这无疑加大的开发人员的开发难度.
如今咱们须要更为方便的解决方案,MongoDB为咱们完美解决此类LBS问题.此篇文章也主要使用SpringData,将spring与MongoDB进行整合.java

二维地图

MongoDB目前支持二维的地图查询,查询区域包括圆形与矩形,距离单位包括MILES,KILOMETERS,NEUTRAL,下面的示例演示距离单位为NEUTRAL,而实际生产应用中则会用到MILES与KILOMETERS.spring

MongoDB示例

首先定义一个位置集合,给定a,b,c,d节点.mongodb

1app

2spa

3hibernate

4code

5xml

6对象

7排序

8

 

> db.createCollection("location")

{ "ok" : 1 }

> db.location.save( {_id: "A", position: [0.1, -0.1]} )

> db.location.save( {_id: "B", position: [1.0, 1.0]} )

> db.location.save( {_id: "C", position: [0.5, 0.5]} )

> db.location.save( {_id: "D", position: [-0.5, -0.5]} )

 

接着指定location索引

1

db.location.ensureIndex( {position: "2d"} )

如今咱们能够进行简单的GEO查询

查询point(0,0),半径0.7附近的点

1

2

3

4

 

> db.location.find( {position: { $near: [0,0], $maxDistance: 0.7  } } )

{ "_id" : "A", "position" : [ 0.1, -0.1 ] }

 

查询point(0,0),半径0.75附近的点

1

2

3

4

5

6

 

> db.location.find( {position: { $near: [0,0], $maxDistance: 0.75  } } )

{ "_id" : "A", "position" : [ 0.1, -0.1 ] }

{ "_id" : "C", "position" : [ 0.5, 0.5 ] }

{ "_id" : "D", "position" : [ -0.5, -0.5 ] }

 

咱们能够看到半径不同,查询出的点也不同,由于c点坐标为[0.5,0.5],c至圆点的距离根据勾股定理可得出Math.sqrt(0.25 +0.25) ≈ 0.707,因此最大距离0.7时查找不到你要的点.

查询[0.25, 0.25], [1.0,1.0]区域附近的点

1

2

3

4

5

 

> db.location.find( {position: { $within: { $box: [ [0.25, 0.25], [1.0,1.0] ] }  } } )

{ "_id" : "C", "position" : [ 0.5, 0.5 ] }

{ "_id" : "B", "position" : [ 1, 1 ] }

 

Spring Data示例

spring data为咱们封装了mongoDB访问接口与实现,咱们能够像使用hibernateTemplate同样使用mongoTemplate.
首先咱们须要像hibernate同样定义pojo类

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

 

import org.springframework.data.annotation.Id;

import org.springframework.data.mongodb.core.mapping.Document;

 

@Document(collection = "location")

public class Location {

 

    @Id

    private String id;

 

    private double[] position;

    

    /** getter setter hashcode equals toString ...  */

 

}

 

定义Dao,咱们先使用最简单的mongoTemplate来实现

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

 

import java.util.List;

 

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.data.mongodb.core.MongoTemplate;

import org.springframework.data.mongodb.core.geo.Box;

import org.springframework.data.mongodb.core.geo.Point;

import org.springframework.data.mongodb.core.query.Criteria;

import org.springframework.data.mongodb.core.query.Query;

import org.springframework.stereotype.Repository;

 

@Repository

public class LocationDao {

 

    @Autowired

    MongoTemplate mongoTemplate;

 

    public List<Location> findCircleNear(Point point, double maxDistance) {

        return mongoTemplate.find(

                new Query(Criteria.where("position").near(point).maxDistance(maxDistance)),

                Location.class);

    }

 

    public List<Location> findBoxNear(Point lowerLeft, Point upperRight) {

        return mongoTemplate.find(

                new Query(Criteria.where("position").within(new Box(lowerLeft, upperRight))),

                Location.class);

    }

 

}

 

最后咱们写一个test类

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

 

import java.util.Collection;

import java.util.List;

 

import org.junit.Before;

import org.junit.Test;

import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.data.mongodb.core.MongoTemplate;

import org.springframework.data.mongodb.core.geo.Point;

import org.springframework.data.mongodb.core.index.GeospatialIndex;

import org.springframework.test.context.ContextConfiguration;

import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

 

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(locations = { "classpath:/applicationContext.xml",

        "classpath:/application-mongo.xml" })

public class MongoDBTest {

 

    @Autowired

    LocationDao locationDao;

 

    @Autowired

    MongoTemplate template;

 

    @Before

    public void setUp() {

        // 等同db.location.ensureIndex( {position: "2d"} )

        template.indexOps(Location.class).ensureIndex(new GeospatialIndex("position"));

        // 初始化数据

        template.save(new Location("A", 0.1, -0.1));

        template.save(new Location("B", 1, 1));

        template.save(new Location("C", 0.5, 0.5));

        template.save(new Location("D", -0.5, -0.5));

    }

 

    @Test

    public void findCircleNearTest() {

        List<Location> locations = locationDao.findCircleNear(new Point(0, 0), 0.7);

        print(locations);

        System.err.println("-----------------------");

        locations = locationDao.findCircleNear(new Point(0, 0), 0.75);

        print(locations);

    }

 

    @Test

    public void findBoxNearTest() {

        List<Location> locations = locationDao.findBoxNear(new Point(0.2, 0.2), new Point(1, 1));

        print(locations);

    }

 

    public static void print(Collection<Location> locations) {

        for (Location location : locations) {

            System.err.println(location);

        }

    }

 

}

 

你们能够看到运行结果与咱们直接在mongoDB上的同样.

MongoRepository

MongoRepository提供了对MongoTemplate的封装与实现,只须要继承MongoRepository接口,填上对应的bean类与ID类型,无需实现里面的方法便可使用,先看代码.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

 

import org.springframework.data.mongodb.core.geo.Box;

import org.springframework.data.mongodb.core.geo.Distance;

import org.springframework.data.mongodb.core.geo.Point;

import org.springframework.data.mongodb.repository.MongoRepository;

 

public interface LocationRepository extends MongoRepository<Location, String> {

 

    List<Location> findByPositionNear(Point p, Distance d);

 

    List<Location> findByPositionWithin(Box b);

 

}

 

而后在test类中引用此类便可,MongoRepository实现了最基本的增删改查的功能,要想增长额外的查询方法,能够按照如下规则定义接口的方法.
自定义查询方法,格式为findBy+字段名+方法名,方法传进的参数即字段的值,此外还支持分页查询,经过传进一个Pageable对象会返回Page集合.
原理相信你们也很清楚,即aop,细节就不说拉.

小提示

near与within方法区别,near方法查询后会对结果集对distance进行排序且有大小限制,而within是无序的也无大小限制.

若是你们有新发现,也可回帖,我会及时补充.

相关文章
相关标签/搜索