mongodb的地理位置索引
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以后将引入新的空间模型.
其他:
The current implementation encodes geographic hash codes atop standard MongoDB b-trees. Results of $near queries are exact. The problem with geohashing is that prefix lookups don't give you exact results, especially around bit flip areas. MongoDB solves this by doing a grid by grid search after the initial prefix scan. This guarantees performance remains very high while providing correct results
mongodb的地理位置索引的更多相关文章
- Mongodb添加地理位置索引
1.同步站点信息到mongo中(支持mysql.sqlserver数据同步) 2.在Collections文件夹下所在文档右键,在菜单中选择Add Index, 3.然后进行数据查询{ "m ...
- 地理位置索引 2d索引
地址位置索引:将一些点的位置存储在mongodb中,创建索引后,可以按照位置来查找其他点 子分类: .2d索引:平面地理位置索引,用于存储和查找平面上的点. .2dsphere索引:球面地理位置索引, ...
- 图解 MongoDB 地理位置索引的实现原理
地理位置索引支持是MongoDB的一大亮点,这也是全球最流行的LBS服务foursquare 选择MongoDB的原因之一.我们知道,通常的数据库索引结构是B+ Tree,如何将地理位置转化为可建立B ...
- 图解 MongoDB 地理位置索引的实现原理(转)
原文链接:图解 MongoDB 地理位置索引的实现原理 地理位置索引支持是MongoDB的一大亮点,这也是全球最流行的LBS服务foursquare 选择MongoDB的原因之一.我们知道,通常的数据 ...
- MongoDB数据模型和索引学习总结
MongoDB数据模型和索引学习总结 1. MongoDB数据模型: MongoDB数据存储结构: MongoDB针对文档(大文件採用GridFS协议)採用BSON(binary json,採用二进制 ...
- MongoDB学习笔记~索引提高查询效率
回到目录 索引这个东西大家不会陌生,只要接触到稍微大一点的数据,都会用到这东西,它可以提升查询的速度,相当代价就是占用了更多的存储空间,这也是正常的,符合“能量守恒定理”,哈哈!今天说的是MongoD ...
- MongoDB学习笔记(索引)
一.索引基础: MongoDB的索引几乎与传统的关系型数据库一模一样,这其中也包括一些基本的优化技巧.下面是创建索引的命令: > db.test.ensureIndex({" ...
- MongoDB的学习--索引
索引可以用来优化查询,而且在某些特定类型的查询中,索引是必不可少的.为集合选择合适的索引是提高性能的关键. 先来mock数据 for (i = 0; i < 1000000; i++) { db ...
- MongoDB学习笔记(索引)(转)
一.索引基础: MongoDB的索引几乎与传统的关系型数据库一模一样,这其中也包括一些基本的优化技巧.下面是创建索引的命令: > db.test.ensureIndex({" ...
随机推荐
- IDEA 使用 SVN的一个注意点
IDEA是调用SVN.EXE来实现相关版本管理功能的,所以必须要安装visualSVN,然后再使用相关功能!
- UVA 515 King
差分约束系统的第一个题目,看了落花大神的博客后,对差分约束有了一定了解,关键在于建图,然后就是判断是否存在负权回路. 关于差分约束系统的解释详见维基百科:http://zh.wikipedia.org ...
- Ubuntu nfs 配置
1. nfs server端的安装和配置 (1)安装nfs server sudo apt-get install nfs-kernel-server nfs-common (2)重启nfs serv ...
- java.lang.Boolean为null时
public class TestBooleanNull { public static void main(String[] args) { if (test()) { System.out.pri ...
- velocity-1.7中vm文件的存放位置
velocity-1.7中关于vm文件存放 demo: public class App_example1 { public App_example1() { String propfile=&quo ...
- laravel route路由,视图和response和filter
Laravel充分利用PHP 5.3的特性,使路由变得简单并富于表达性.这使得从构建API到完整的web应用都变得尽可能容易.路由的实现代码在 application/routes.php 文件. 和 ...
- 【HDOJ】1238 Substrings
深搜+剪枝,简单字符串. #include <stdio.h> #include <string.h> #define MAXLEN 105 #define MAXNUM 10 ...
- -_-#【CSS 优化】
高性能CSS 关于css通配符性能问题不完全测试 CSS的渲染效率 border: none; /* 不写 border: 0; 但几乎都是写 border: 0;的.. */ 不要使用过小的图片做背 ...
- 物联网操作系统HelloX V1.77(beta)版本发布
物联网操作系统HelloX V1.77发布 经过近半年的努力,物联网操作系统HelloX V1.77版本正式完成,源代码已上载到github(github.com/hellox-project/Hel ...
- Google Chrome中的高性能网络 (三)
使用预连接优化了TCP连接管理 已经预解析到了主机名,也有了由OmniBox和Chrome Predictor提供信号,预示着用户未来的操作.为什么再进一步连接到目标主机,在用户真正发起请求前完成TC ...