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的地理位置索引的更多相关文章

  1. Mongodb添加地理位置索引

    1.同步站点信息到mongo中(支持mysql.sqlserver数据同步) 2.在Collections文件夹下所在文档右键,在菜单中选择Add Index, 3.然后进行数据查询{ "m ...

  2. 地理位置索引 2d索引

    地址位置索引:将一些点的位置存储在mongodb中,创建索引后,可以按照位置来查找其他点 子分类: .2d索引:平面地理位置索引,用于存储和查找平面上的点. .2dsphere索引:球面地理位置索引, ...

  3. 图解 MongoDB 地理位置索引的实现原理

    地理位置索引支持是MongoDB的一大亮点,这也是全球最流行的LBS服务foursquare 选择MongoDB的原因之一.我们知道,通常的数据库索引结构是B+ Tree,如何将地理位置转化为可建立B ...

  4. 图解 MongoDB 地理位置索引的实现原理(转)

    原文链接:图解 MongoDB 地理位置索引的实现原理 地理位置索引支持是MongoDB的一大亮点,这也是全球最流行的LBS服务foursquare 选择MongoDB的原因之一.我们知道,通常的数据 ...

  5. MongoDB数据模型和索引学习总结

    MongoDB数据模型和索引学习总结 1. MongoDB数据模型: MongoDB数据存储结构: MongoDB针对文档(大文件採用GridFS协议)採用BSON(binary json,採用二进制 ...

  6. MongoDB学习笔记~索引提高查询效率

    回到目录 索引这个东西大家不会陌生,只要接触到稍微大一点的数据,都会用到这东西,它可以提升查询的速度,相当代价就是占用了更多的存储空间,这也是正常的,符合“能量守恒定理”,哈哈!今天说的是MongoD ...

  7. MongoDB学习笔记(索引)

    一.索引基础:    MongoDB的索引几乎与传统的关系型数据库一模一样,这其中也包括一些基本的优化技巧.下面是创建索引的命令:    > db.test.ensureIndex({" ...

  8. MongoDB的学习--索引

    索引可以用来优化查询,而且在某些特定类型的查询中,索引是必不可少的.为集合选择合适的索引是提高性能的关键. 先来mock数据 for (i = 0; i < 1000000; i++) { db ...

  9. MongoDB学习笔记(索引)(转)

    一.索引基础:    MongoDB的索引几乎与传统的关系型数据库一模一样,这其中也包括一些基本的优化技巧.下面是创建索引的命令:    > db.test.ensureIndex({" ...

随机推荐

  1. Kerberos ticket lifetime及其它

    前言 之前的博文中涉及到了Kerberos的内容,这里对Kerberos ticket lifetime相关的内容做一个补充. ticket lifetime Kerberos ticket具有lif ...

  2. 终于把你必须知道的.NET看完了

    终于把你必须知道的.NET看完了,第二步就是把精通ASP.NET MVC3框架这本书搞定,练习MVC3的使用,并把EF,LINQ也练习一下,期间要做一个项目“多用户微信公众平台”项目,最近微信公众平台 ...

  3. Spring MVC 返回NULL时客户端用$.getJSON的问题

    如果Spring MVC返回是NULL,那么客户端的$.getJSON就不会触发: 必须返回点什么东西: 如果返回的是一个字符串,客户端的$.getJSON也不会触发:把字符串 包装成List< ...

  4. Mybatis异常There is no getter for property named &#39;XXX&#39; in &#39;class com.xxx.xxx.UserAccountDTO

    mybatis报错异常信息如下: 解决: 在接口中加上注解:@Param("userAccountDTO"),如图

  5. java this的用法

    this 含义:代表当前对象 用法: 用于返回对象的引用 示例代码 public class Test { public Test f() { return this;//获取当前对象的引用 } pu ...

  6. 解决VMware下CentOS连不上网络问题

    https://blog.csdn.net/wangmx1993328/article/details/80897533

  7. 97.5%准确率的深度学习中文分词(字嵌入+Bi-LSTM+CRF)

    本文转载自:http://www.17bigdata.com/97-5%E5%87%86%E7%A1%AE%E7%8E%87%E7%9A%84%E6%B7%B1%E5%BA%A6%E5%AD%A6%E ...

  8. 遇见CUBA CLI

    原文:Meet CLI for CUBA Platform 翻译:CUBA China CUBA-Platform 官网 : https://www.cuba-platform.com CUBA Ch ...

  9. java笔记--线程的插队行为

    对线程的插队行为的理解 在编写多线程时,会遇到让一个线程优先于其他线程运行的情况, 此时除了可以设置其优先级高于其他线程外,更直接的方式是使用Thread类的join()方法 --如果朋友您想转载本文 ...

  10. 20155226 2016-2017-2 《Java程序设计》第9周学习总结

    20155226 2016-2017-2 <Java程序设计>第9周学习总结 教材学习内容总结 JDBC简介 JDBC是用于执行SQL的解决方案,开发人员使用JDBC的标准接口,数据库厂商 ...