前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >持续搞【附近】系列---听说MongoDB是专业的(三)

持续搞【附近】系列---听说MongoDB是专业的(三)

作者头像
老李秀
发布2019-11-13 00:16:06
5530
发布2019-11-13 00:16:06
举报

还是得好用才行

一直听说MongoDB才是【专业】搞地理空间查询的,人家才是【专业】的!相当长一段时间来,一说搞【附近】就会相当一批人的脑海里就不自主浮想到MongoDB... ...

上一节中的geohash顶多能应付一下点的运算,本质上是将二维的数据一维化然后通过索引提高预算查询效率,相比之下MongoDB最大的优势和优点就是:

  • 效率高很多
  • 支持多点、线、多边形
  • 球面运算

按说吧,上面划横线的才是榜样模板式的回答,然而实际上对于我们这个庞大的泥腿子群体而言,MongoDB最大的优势是:

复制粘贴一下demo代码,CURD就能用

MongoDB的地理空间索引分为两种类型:

  • 2d索引,用于平面地图之流,反正也能用
  • 2dsphere索引,用于地球儿表面的地理查询运算,推荐用法

先说2d索引,然而实际上MongoDB的2d索引的实现底层原理依然是geohash,所以同样其2d索引支持点的存储运算,对于线和面就相对比较难受了(PS:由于市面上好像并没有看到名字类似于《MongoDB内核分析》或《MongoDB设计与实现》的书籍,所以对于MongoDB的2d索引的实现结论我的印象停留在【几年前】取自于mongo官网的一篇blog)。所以,既然你都用MongoDB了,直接一步到位走2dsphere就行了。

2dsphere的实现并不是geo-hash,我依然是从MongoDB官网的blog上了解到的一些信息和资料。2dsphere采用的是【谷人希】公司的google S2算法,将S2处理好的索引数据保存在了B-Tree数据结构中,B-Tree可以支持快速查询。

如果有曾经深入研究过MongoDB这两种地理空间索引实现的老哥们,可以公众号发消息帮我double check一下是否正确。毕竟官方的blog资料发表的年份都是比较老的,不敢保证这些年新版本MongoDB是否依然如此。

然而,下面我并不打算剖析【谷人希】公司的S2算法的大概原理(自己理解并不算深刻),也并不打算科普B-Tree和BTree以及B+Tree的区别(太烦人了)。后面我会抽空专门整理一篇关于标题类似于《人类关于N种地理空间索引实现方案横向大评测》之类的文章,毕竟,当年为了搞【附近】我是曾经下过真功夫的。

实际上从上面文字中可以看出,IT人拼到最后全是算法和数据结构

这个和桃儿说过的“ 艺人拼到最后拼的是文化 ”基本上是很类似的。

在MongoDB中,2dsphere或2d是描述是由一种叫做geoJSON的标准格式来描述的,从名字上就可以看出来首先它是一坨JSON,其次它定义了自己的一些标准。比如我们要用geoJSON来描述一个点、一条线,就应该用如下方式进行描述:

代码语言:javascript
复制
// 描述一个点"loc":{  "type":"Point",  "coordinates":[ 116.55944824218749,30.58827267102698 ]}// 描述一个多边形"box":{  "type":"Polygon",  "coordinates":[                  [                     [116.40701293945311,30.454001045389525],                    [116.77505493164062,30.454001045389525],                    [116.77505493164062,30.76248901825541],                    [116.40701293945311,30.76248901825541],                    [116.40701293945311,30.454001045389525]                  ]                ]}

确切说,除了MongoDB外,还有很多支持地理空间索引的数据库或引擎都会支持geoJSON标准。

下面进入到我们最喜欢的复制粘贴代码阶段,正式开始前我们需要说明下世界上最好的语言和MongoDB之间不得不说的故事。MongoDB为PHP提供了两个版本的驱动:

  • mongodb,支持PHP7,持续支持更新中
  • mongo,仅支持PHP5,目前只管修bug其他统统不管

这两个驱动的用法完全不一样,然而我不得不承认明显PHP5版本的驱动使用起来更符合人类胃口,PHP7版本简直是要人命。

普及一下,像这种驱动提供的API都是low-level API,为了更加方便地协助我们搞花式CRUD,我从github上找了一个基于PHP7 MongoDB low-level-API包装了一层的high-level-API library。这个东西同样也是MongoDB官方出品,地址如下:

https://github.com/mongodb/mongo-php-library

下面我们将使用上面这个库演示如何使用MongoDB的2dsphere搞【附近】

第一步:创建2dphere索引

因为MongoDB是带有KV性质的文档型数据库,所以有一点儿和MySQL非常不一样的就是:不需要提前定义数据库字段。在正式使用2dsphere索引之前,我们要做的就是首先在【某个字段】上创建一个2dsphere索引,大概就是下面这样:

代码语言:javascript
复制
<?php// 加入composer支持require_once __DIR__ . "/vendor/autoload.php";// 这句相当于使用momo数据库,然后使用user表$collection = ( new MongoDB\Client )->momo->user;// 在loc字段上创建2dsphere索引// 尽管还没有loc字段,不过这并不重要$result = $collection->createIndex( array('loc' => '2dsphere',) );var_dump( $result );

上面代码保存为index.php,然后php index.php执行,结果你们感受一下:

第二步:搞一坨测试数据

然后我们围绕在北京附近创建一坨测试数据,代码如下:(我知道肯定有人看到下面这一坨代码后会想到BulkWrite,你就当我是个蠢货)

代码语言:javascript
复制
<?php// composerrequire_once __DIR__ . "/vendor/autoload.php";// 这句相当于使用momo数据库,然后使用user表$collection = ( new MongoDB\Client )->momo->user;// 然后插入10000数据即可for ( $i = 1; $i <= 10000; $i++ ) {// 生成一个维度  $latitude  = mt_rand( 38, 40 ).'.'.mt_rand( 100000, 999999 );// 生成一个经度  $longitude = mt_rand( 115, 116 ).'.'.mt_rand( 100000, 999999 );  $insert = $collection->insertOne( array(// 你可以粗暴认为:_id就是mongodb的主键,如果你不显式为_id赋值// 那么mongodb将会自动会_id生成一坨类似于uuid的值'_id' => $i,// 注意这个loc字段对应上面创建索引的字段名'loc' => array(// type为point,表示是点,除此之外还有Line和Polygon两种类型'type'        => 'Point',// 经度、维度'coordinates' => array( floatval( $longitude ), floatval( $latitude ) ),    ),  ) );  var_dump( $insert );}

php index.php执行,结果你们感受一下:

第三步:开始搞【附近】

我们将围绕经纬度(116.2092590332,40.0444375846)进行查找,为了对演示结果心里有谱,请你将(116.2092590332,40.0444375846)也插入到MongoDB中,这样查询结果中排名离你最近的一定会是该用户咯~

代码语言:javascript
复制
<?phprequire_once __DIR__ . "/vendor/autoload.php";// 这句相当于使用momo数据库$database = ( new MongoDB\Client )->momo;// command方法相当于直接执行mongodb原生语句// 因为我懒的看这个mongodb-library的库语法了$cursor = $database->command( array('geoNear' => 'user','near'    => array('type'        => 'Point', 'coordinates' => array(116.2092590332,40.0444375846    ),    ),  'num' => 5,  ) );$rets = $cursor->toArray()[0];foreach( $rets->results as $r ) { echo $r->obj['_id'].'号用户距离您 : '.$r->dis.'米'.PHP_EOL;}

php index.php执行,结果你们感受一下:

距离0米,这说明老子的代码不是TM瞎写的,绝对能用!

实际上,如果说我们把【xxxx号用户】当作是【牌照xxxx的出租车】的话,一般最粗暴版本的【搞附近的车】业务就基本上得到实现了。

好了,本节暂时到此。周六日码文章,真的是好困!

本文关键词

MongoDB、2dsphere,Google S2、B-Tree、geoJSON

本文代码github

https://github.com/elarity/wechat-official-accounts-demo-code

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-06-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 高性能API社区 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 MongoDB
腾讯云数据库 MongoDB(TencentDB for MongoDB)是腾讯云基于全球广受欢迎的 MongoDB 打造的高性能 NoSQL 数据库,100%完全兼容 MongoDB 协议,支持跨文档事务,提供稳定丰富的监控管理,弹性可扩展、自动容灾,适用于文档型数据库场景,您无需自建灾备体系及控制管理系统。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档