/首页
/开源
/关于
持续搞【附近】系列---听说MongoDB是专业的(三)
发表@2019-09-18 23:48:45
更新@2023-01-21 22:47:40
####
还是得好用才行
一直听说MongoDB才是【专业】搞地理空间查询的,人家才是【专业】的!相当长一段时间来,一说搞【附近】就会相当一批人的脑海里就不自主浮想到MongoDB... ... ![](https://ti-node.com/static/upload/6580111476546076673) 上一节中的geohash顶多能应付一下点的运算,本质上是将二维的数据一维化然后通过索引提高预算查询效率,相比之下MongoDB最大的优势和优点就是: - 效率高很多 - 支持多点、线、多边形 - 球面运算 按说吧,上面划横线的才是榜样模板式的回答,然而实际上对于我们这个庞大的泥腿子群体而言,MongoDB最大的优势是: ####
复制粘贴一下demo代码,CURD就能用
![](https://ti-node.com/static/upload/6580111640543363073) 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来描述一个点、一条线,就应该用如下方式进行描述: ```php // 描述一个点 "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索引,大概就是下面这样: ```php momo->user; // 在loc字段上创建2dsphere索引 // 尽管还没有loc字段,不过这并不重要 $result = $collection->createIndex( array( 'loc' => '2dsphere', ) ); var_dump( $result ); ``` 上面代码保存为index.php,然后php index.php执行,结果你们感受一下: ![](https://ti-node.com/static/upload/6580112292921212928) ------------ ####
第二步:搞一坨测试数据
然后我们围绕在北京附近创建一坨测试数据,代码如下:(我知道肯定有人看到下面这一坨代码后会想到BulkWrite,你就当我是个蠢货) ```php 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执行,结果你们感受一下: ![](https://ti-node.com/static/upload/6580112564531757057) ------------ ####
第三步:开始搞【附近】
我们将围绕经纬度(116.2092590332,40.0444375846)进行查找,为了对演示结果心里有谱,请你将(116.2092590332,40.0444375846)也插入到MongoDB中,这样查询结果中排名离你最近的一定会是该用户咯~ ```php 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执行,结果你们感受一下: ![](https://ti-node.com/static/upload/6580112760967790593) 距离0米,这说明老子的代码不是TM瞎写的,绝对能用! 实际上,如果说我们把【xxxx号用户】当作是【牌照xxxx的出租车】的话,一般最粗暴版本的【搞附近的车】业务就基本上得到实现了。 好了,本节暂时到此。周六日码文章,真的是好困! ------------ 本文关键词:MongoDB、2dsphere,Google S2、B-Tree、geoJSON 本文代码github:https://github.com/elarity/wechat-official-accounts-demo-code