您的位置:首页 > 数据库 > Mongodb

MongoDB位置查询

2015-09-10 11:37 603 查看


基本介绍

位置服务(LBS)解决的主要问题是当前位置周围某个范围内的人或场所.

在传统的解决方案,开发人员需要根据复杂的几何运算与大量的SQL语句进行查找,这无疑加大的开发人员的开发难度.

现在我们需要更为方便的解决方案,MongoDB为我们完美解决此类LBS问题.此篇文章也主要使用SpringData,将spring与MongoDB进行整合.


二维地图

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


MongoDB示例

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

1

2

3

4

5

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

如果大家有新发现,也可回帖,我会及时补充.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: