设为首页 收藏本站
查看: 1395|回复: 0

[经验分享] mongo DB空间索引

[复制链接]

尚未签到

发表于 2015-11-11 09:45:26 | 显示全部楼层 |阅读模式
  http://blog.iyunv.com/raojinpg/article/details/6891507
  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,不过测试可以建多个,如下

> db.mapinfo.drop()

true

> db.mapinfo.insert({"category" : "bank","name" : "china people bank","loc" : [71,81],"HQ_loc" : [91,101]})

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

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

> db.mapinfo.getIndexes()

[

{

"name" : "_id_",

"ns" : "test.mapinfo",

"key" : {

"_id" : 1

}

},

{

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

"ns" : "test.mapinfo",

"key" : {

"loc" : "2d"

},

"name" : "loc_",

"background" : "true"

},

{

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

"ns" : "test.mapinfo",

"key" : {

"HQ_loc" : "2d"

},

"name" : "HQ_loc_",

"background" : "true"

}

]

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

{ "_id" : ObjectId("4d2439643238ba30f9ca05cc"), "category" : "bank", "name" : "china people bank", "loc" : [ 71, 81 ], "HQ_loc" : [ 91, 101 ] }

> db.mapinfo.find({"HQ_loc" : {"$near" : [20,21]}})

{ "_id" : ObjectId("4d2439643238ba30f9ca05cc"), "category" : "bank", "name" : "china people bank", "loc" : [ 71, 81 ], "HQ_loc" : [ 91, 101 ] }



# 使用范例2:

# 测试数据

> db.mapinfo.find()

{ "_id" : ObjectId("4d2439643238ba30f9ca05cc"), "category" : "bank", "name" : "china people bank", "loc" : [ 71, 81 ], "HQ_loc" : [ 91, 101 ] }

{ "_id" : ObjectId("4d243a743238ba30f9ca05cf"), "category" : "coffee", "name" : "digoal coffee bar", "loc" : [ 100, 81 ], "HQ_loc" : [ 100, 101 ] }

{ "_id" : ObjectId("4d243a8b3238ba30f9ca05d0"), "category" : "tea", "name" : "digoal tea bar", "loc" : [ 110, 81 ], "HQ_loc" : [ 110, 101 ] }

{ "_id" : ObjectId("4d243ab23238ba30f9ca05d1"), "category" : "shop", "name" : "digoal supermarket", "loc" : [ 120, 81 ], "HQ_loc" : [ 120, 101 ] }

{ "_id" : ObjectId("4d243aba3238ba30f9ca05d2"), "category" : "shop", "name" : "digoal supermarket1", "loc" : [ 121, 81 ], "HQ_loc" : [ 120, 101 ] }

{ "_id" : ObjectId("4d243abe3238ba30f9ca05d3"), "category" : "shop", "name" : "digoal supermarket2", "loc" : [ 122, 81 ], "HQ_loc" : [ 120, 101 ] }

{ "_id" : ObjectId("4d243ac33238ba30f9ca05d4"), "category" : "shop", "name" : "digoal supermarket3", "loc" : [ 123, 81 ], "HQ_loc" : [ 120, 101 ] }

{ "_id" : ObjectId("4d243ac83238ba30f9ca05d5"), "category" : "shop", "name" : "digoal supermarket4", "loc" : [ 124, 81 ], "HQ_loc" : [ 120, 101 ] }

{ "_id" : ObjectId("4d243ace3238ba30f9ca05d6"), "category" : "shop", "name" : "digoal supermarket5", "loc" : [ 125, 81 ], "HQ_loc" : [ 120, 101 ] }

{ "_id" : ObjectId("4d243ad63238ba30f9ca05d7"), "category" : "shop", "name" : "digoal supermarket6", "loc" : [ 126, 81 ], "HQ_loc" : [ 120, 101 ] }

{ "_id" : ObjectId("4d243aee3238ba30f9ca05d8"), "category" : "shop", "name" : "digoal supermarket7", "loc" : [ 26, 81 ], "HQ_loc" : [ 120, 101 ] }

{ "_id" : ObjectId("4d243af43238ba30f9ca05d9"), "category" : "shop", "name" : "digoal supermarket8", "loc" : [ 27, 81 ], "HQ_loc" : [ 120, 101 ] }

{ "_id" : ObjectId("4d243af93238ba30f9ca05da"), "category" : "shop", "name" : "digoal supermarket9", "loc" : [ 29, 81 ], "HQ_loc" : [ 120, 101 ] }

{ "_id" : ObjectId("4d243aff3238ba30f9ca05db"), "category" : "shop", "name" : "digoal supermarket10", "loc" : [ 30, 81 ], "HQ_loc" : [ 120, 101 ] }

{ "_id" : ObjectId("4d243b063238ba30f9ca05dc"), "category" : "shop", "name" : "digoal supermarket11", "loc" : [ 31, 81 ], "HQ_loc" : [ 120, 101 ] }

# 索引

> db.mapinfo.getIndexes()

[

{

"name" : "_id_",

"ns" : "test.mapinfo",

"key" : {

"_id" : 1

}

},

{

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

"ns" : "test.mapinfo",

"key" : {

"loc" : "2d"

},

"name" : "loc_",

"background" : "true"

},

{

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

"ns" : "test.mapinfo",

"key" : {

"HQ_loc" : "2d"

},

"name" : "HQ_loc_",

"background" : "true"

}

]

# 查询离[50,50]最近的5家商店

> db.mapinfo.find({"loc" : {"$near" : [50,50]},"category" : "shop"}).limit(5)

{ "_id" : ObjectId("4d243b063238ba30f9ca05dc"), "category" : "shop", "name" : "digoal supermarket11", "loc" : [ 31, 81 ], "HQ_loc" : [ 120, 101 ] }

{ "_id" : ObjectId("4d243aff3238ba30f9ca05db"), "category" : "shop", "name" : "digoal supermarket10", "loc" : [ 30, 81 ], "HQ_loc" : [ 120, 101 ] }

{ "_id" : ObjectId("4d243af93238ba30f9ca05da"), "category" : "shop", "name" : "digoal supermarket9", "loc" : [ 29, 81 ], "HQ_loc" : [ 120, 101 ] }

{ "_id" : ObjectId("4d243af43238ba30f9ca05d9"), "category" : "shop", "name" : "digoal supermarket8", "loc" : [ 27, 81 ], "HQ_loc" : [ 120, 101 ] }

{ "_id" : ObjectId("4d243aee3238ba30f9ca05d8"), "category" : "shop", "name" : "digoal supermarket7", "loc" : [ 26, 81 ], "HQ_loc" : [ 120, 101 ] }

# 找出限制离[50,50]在37 的商店,使用maxDistance

> db.mapinfo.find({"loc" : {"$near" : [50,50], "$maxDistance" : 37},"category" : "shop"})

{ "_id" : ObjectId("4d243b063238ba30f9ca05dc"), "category" : "shop", "name" : "digoal supermarket11", "loc" : [ 31, 81 ], "HQ_loc" : [ 120, 101 ] }

{ "_id" : ObjectId("4d243aff3238ba30f9ca05db"), "category" : "shop", "name" : "digoal supermarket10", "loc" : [ 30, 81 ], "HQ_loc" : [ 120, 101 ] }

# 复合索引

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

> db.mapinfo.getIndexes()

[

{

"name" : "_id_",

"ns" : "test.mapinfo",

"key" : {

"_id" : 1

}

},

{

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

"ns" : "test.mapinfo",

"key" : {

"loc" : "2d"

},

"name" : "loc_",

"background" : "true"

},

{

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

"ns" : "test.mapinfo",

"key" : {

"HQ_loc" : "2d"

},

"name" : "HQ_loc_",

"background" : "true"

},

{

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

"ns" : "test.mapinfo",

"key" : {

"loc" : "2d",

"category" : 1

},

"name" : "loc__category_1"

}

]



3. 范例 3

# 除了使用find来搜索以外,还可以使用runCommand

> db.runCommand({"geoNear" : "mapinfo","near" : [50,50],"num" : 10})

{ "errmsg" : "more than 1 geo indexes :(", "ok" : 0 }

# 这里报错,原因是mapinfo超过一个2d索引,但是使用find来查询不会报错,

# 只保留一个“2d"索引后,使用runCommand正常

> db.mapinfo.dropIndex({"loc" : "2d","category" : 1})

{ "nIndexesWas" : 4, "ok" : 1 }

> db.runCommand({"geoNear" : "mapinfo","near" : [50,50],"num" : 10})

{ "errmsg" : "more than 1 geo indexes :(", "ok" : 0 }

> db.mapinfo.dropIndex({"HQ_loc" : "2d"})

{ "nIndexesWas" : 3, "ok" : 1 }

# "num" 限制返回的记录数

# 使用runCommand和geoNear的好处是可以返回距离.本例"dis" : 36.3593194466869,

> db.runCommand({"geoNear" : "mapinfo","near" : [50,50],"num" : 1})

{

"ns" : "test.mapinfo",

"near" : "1100110000001111110000001111110000001111110000001111",

"results" : [

{

"dis" : 36.3593194466869,

"obj" : {

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

"category" : "shop",

"name" : "digoal supermarket11",

"loc" : [

31,

81

],

"HQ_loc" : [

120,

101

]

}

}

],

"stats" : {

"time" : 0,

"btreelocs" : 6,

"nscanned" : 7,

"objectsLoaded" : 3,

"avgDistance" : 36.3593194466869,

"maxDistance" : 36.3593194466869

},

"ok" : 1

}

# 使用runCommand同样也可以使用普通的FIND的限制条件,如下放在query : { "category" : "coffee" }

> db.runCommand({"geoNear" : "mapinfo","near" : [50,50],"num" : 1,query : { "category" : "coffee" }})

{

"ns" : "test.mapinfo",

"near" : "1100110000001111110000001111110000001111110000001111",

"results" : [

{

"dis" : 58.830266786369556,

"obj" : {

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

"category" : "coffee",

"name" : "digoal coffee bar",

"loc" : [

100,

81

],

"HQ_loc" : [

100,

101

]

}

}

],

"stats" : {

"time" : 0,

"btreelocs" : 15,

"nscanned" : 15,

"objectsLoaded" : 7,

"avgDistance" : 58.830266786369556,

"maxDistance" : 58.830266786369556

},

"ok" : 1

}



4. 范例4

# 空间索引还支持范围搜索,目前支持圆和矩阵的范围

# 使用box

> box = [[19,19],[90,90]]

[ [ 19, 19 ], [ 90, 90 ] ]

> db.mapinfo.find({"loc" : {"$within" : {"$box" : box}}})

{ "_id" : ObjectId("4d2439643238ba30f9ca05cc"), "category" : "bank", "name" : "china people bank", "loc" : [ 71, 81 ], "HQ_loc" : [ 91, 101 ] }

{ "_id" : ObjectId("4d243b063238ba30f9ca05dc"), "category" : "shop", "name" : "digoal supermarket11", "loc" : [ 31, 81 ], "HQ_loc" : [ 120, 101 ] }

{ "_id" : ObjectId("4d243aff3238ba30f9ca05db"), "category" : "shop", "name" : "digoal supermarket10", "loc" : [ 30, 81 ], "HQ_loc" : [ 120, 101 ] }

{ "_id" : ObjectId("4d243af93238ba30f9ca05da"), "category" : "shop", "name" : "digoal supermarket9", "loc" : [ 29, 81 ], "HQ_loc" : [ 120, 101 ] }

{ "_id" : ObjectId("4d243af43238ba30f9ca05d9"), "category" : "shop", "name" : "digoal supermarket8", "loc" : [ 27, 81 ], "HQ_loc" : [ 120, 101 ] }

{ "_id" : ObjectId("4d243aee3238ba30f9ca05d8"), "category" : "shop", "name" : "digoal supermarket7", "loc" : [ 26, 81 ], "HQ_loc" : [ 120, 101 ] }

# 使用center point and radius

> center = [29,81]

[ 29, 81 ]

> radius = 10

10

> db.mapinfo.find({"loc" : {"$within" : {"$center" : [center,radius]}}})

{ "_id" : ObjectId("4d243af93238ba30f9ca05da"), "category" : "shop", "name" : "digoal supermarket9", "loc" : [ 29, 81 ], "HQ_loc" : [ 120, 101 ] }

{ "_id" : ObjectId("4d243af43238ba30f9ca05d9"), "category" : "shop", "name" : "digoal supermarket8", "loc" : [ 27, 81 ], "HQ_loc" : [ 120, 101 ] }

{ "_id" : ObjectId("4d243aff3238ba30f9ca05db"), "category" : "shop", "name" : "digoal supermarket10", "loc" : [ 30, 81 ], "HQ_loc" : [ 120, 101 ] }

{ "_id" : ObjectId("4d243b063238ba30f9ca05dc"), "category" : "shop", "name" : "digoal supermarket11", "loc" : [ 31, 81 ], "HQ_loc" : [ 120, 101 ] }

{ "_id" : ObjectId("4d243aee3238ba30f9ca05d8"), "category" : "shop", "name" : "digoal supermarket7", "loc" : [ 26, 81 ], "HQ_loc" : [ 120, 101 ] }



注意事项:

1. mongoDB处理的是平面距离,但是实际生活中如果涉及到大范围的距离搜索,可能会有偏差,因为地球是球型的。The current implementation assumes an idealized model of a flat earth, meaning that an arcdegree of latitude (y) and longitude (x) represent the same distance everywhere. This is only true at the equator
where they are both about equal to 69 miles or 111km. However, at the 10gen offices at
{ x : -74 , y : 40.74 } one arcdegree of longitude is about 52 miles or 83 km (latitude is unchanged). This means that something 1 mile to the north would seem closer than something 1 mile to the east.

2. 2d索引目前还不支持sharding,In the meantime sharded clusters can use geospatial indexes for unsharded collections within the cluster.

3. New Spherical Model,1.7.0以后将引入新的空间模型.

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-137774-1-1.html 上篇帖子: mongoDB数据库的查询操作 下篇帖子: 利用pymongo操作mongoDB数据库
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表