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是無序的也無大小限制.

若是你們有新發現,也可回帖,我會及時補充.

相關文章
相關標籤/搜索