您的位置:首页 > 其它

大型的LBS应用地理位置索引技术

2013-05-07 17:26 295 查看
地理位置索引支持是MongoDB的一大亮点,这也是全球最流行的LBS服务foursquare 选择MongoDB的原因之一。我们知道,通常的数据库索引结构是B+ Tree,如何将地理位置转化为可建立B+Tree的形式,下文将为你描述。

首先假设我们将需要索引的整个地图分成16×16的方格,如下图(左下角为坐标0,0 右上角为坐标16,16):

如果我们使用sql,是不是要把整个中国地图,进行划分呢?如果有区域的概念,那应该不一个地区的地图经纬度选择出来,然后划分。

比如可以先按照距离基本来过滤,然后在一个大的矩形内进行 距离的计算,最后选择最近的N的点//// 建立一个二维空间索引

mongoDB支持二维空间索引,使用空间索引,mongoDB支持一种特殊查询,如某地图网站上可以查找离你最近的咖啡厅,银行等信息。这个使用mongoDB的空间索引结合特殊的查询方法很容易实现。

前提条件:

建立空间索引的key可以使用array或内嵌文档存储,但是前两个elements必须存储固定的一对空间位置数值。如

{ loc : [ 50 , 30 ] }
{ loc : { x : 50 , y : 30 } }
{ loc : { foo : 50 , y : 30 } }
{ loc : { lat : 40.739037, long: 73.992964 } }

# 使用范例1:

> db.mapinfo.drop()

true

> db.mapinfo.insert({"category" : "coffee","name" : "digoal coffee bar","loc" : [70,80]})

> db.mapinfo.insert({"category" : "tea","name" : "digoal tea bar","loc" : [70,80]})

> db.mapinfo.insert({"category" : "tea","name" : "hangzhou tea bar","loc" : [71,81]})

> db.mapinfo.insert({"category" : "coffee","name" : "hangzhou coffee bar","loc" : [71,81]})

# 未创建2d索引时,不可以使用$near进行查询

> db.mapinfo.find({loc : {$near : [50,50]}})

error: {

"$err" : "can't find special index: 2d for: { loc: { $near: [ 50.0, 50.0 ] } }",

"code" : 13038

}

# 在loc上面创建2d索引

> db.mapinfo.ensureIndex({"loc" : "2d"},{"background" : true})

> db.mapinfo.getIndexes()

[

{

"name" : "_id_",

"ns" : "test.mapinfo",

"key" : {

"_id" : 1

}

},

{

"_id" : ObjectId("4d242e1f3238ba30f9ca05ad"),

"ns" : "test.mapinfo",

"key" : {

"loc" : "2d"

},

"name" : "loc_",

"background" : true

}

]

# 查询测试,返回结果按照从最近到最远的顺序排序输出.

> db.mapinfo.find({loc : {$near : [72,82]},"category" : "coffee"}).explain()

{

"cursor" : "GeoSearchCursor",

"nscanned" : 2,

"nscannedObjects" : 2,

"n" : 2,

"millis" : 0,

"indexBounds" : {

}

}

> db.mapinfo.find({loc : {$near : [72,82]},"category" : "coffee"})

{ "_id" : ObjectId("4d242dce3238ba30f9ca05ac"), "category" : "coffee", "name" : "hangzhou coffee bar", "loc" : [ 71, 81 ] }

{ "_id" : ObjectId("4d242d8b3238ba30f9ca05a9"), "category" : "coffee", "name" : "digoal coffee bar", "loc" : [ 70, 80 ] }

# 换一个经纬度后结果相反.

> db.mapinfo.find({loc : {$near : [69,69]},"category" : "coffee"})

{ "_id" : ObjectId("4d242d8b3238ba30f9ca05a9"), "category" : "coffee", "name" : "digoal coffee bar", "loc" : [ 70, 80 ] }

{ "_id" : ObjectId("4d242dce3238ba30f9ca05ac"), "category" : "coffee", "name" : "hangzhou coffee bar", "loc" : [ 71, 81 ] }

# 2d默认取值范围[-179,-179]到[180,180] 包含这两个点,超出范围将报错

> db.mapinfo.insert({"category" : "bank","name" : "china people bank","loc" : [181,181]})

point not in range

> db.mapinfo.insert({"category" : "bank","name" : "china people bank","loc" : [-179,-180]})

in > 0

# 如果已经存在超过范围的值,建2D索引将报错

> db.mapinfo.insert({"category" : "bank","name" : "china people bank","loc" : [-180,-180]})

> db.mapinfo.ensureIndex({"loc" : "2d"})

in > 0

# 在建2d索引的时候可以指定取值范围

# 如,以上包含了[-180,-180]这个点之后,建2d索引将报错,使用以下解决.或者把这条记录先处理掉.

# 在限制条件下,min不包含,max包含,从下面建索引的语句中可以看出.

> db.mapinfo.ensureIndex({"loc" : "2d"},{min:-181,max:180})

> 成功

# 注意官方文档上说you can only have 1 geo2d index per collection right now,不过测试可以建多个,如下
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: