mongodb通过$geoNear进行坐标检索

最近的项目涉及带geo信息的多源数据融合,发现mongo3.x的geo检索跟2.x有点变化,在这里mark一下。

//1.建立geo索引 db.point.ensureIndex({"geo":"2d"}); //"geo"格式说明: [ 116.279943 #lng经度   40.049971, #lat纬度 ] //2.插入示例数据 db.point.insert({ "@id" : 1, "@geo" : [ 116.305297, 40.041993 ], "@bucket":["稻香村"], "@bucket_sign":["19d40bb2165c198560278e7bc3b2c5e5"] ,"name":"稻香村上地店"}) db.point.insert(

阅读全文

让python调用mongo读写速度加速10倍的方法

1.把mongo读写封装成api

2.在api初始化时保持数据库长链接;并且用线程每2分钟遍历一次所有的表并count一次

import sys import time import pymongo import json import log import traceback import threading //库名test,表名test_table server_list = ['test-mongos.all.serv:6365' for i in range(8)] conn_str = "mongodb://test:123456@" + ",".join(server_list) + "/test" print conn_str conn = pymongo.MongoClient(conn_str) test = conn.test def load_cache(){

阅读全文

从PHP客户端看MongoDB通信协议

MongoDB 的 PHP 客户端有一个 MongoCursor 类,它是用于获取一次查询结果集的句柄(或者叫游标),这个简单的取数据操作,内部实现其实不是那么简单。本文就通过对 MongoCursor 类一些操作进行分析,向大家揭开 MongoDB 客户端服务器通信的一些内部细节。

getNext与网络请求

通常来说,每一次find操作都会返回一个MongoCursor对象,在这个对象上调用getNext方法,就能够获得一条结果数据。循环调用getNext方法就能获取多条数据。下面我们就来看看其内部取数据的具体逻辑。

首先我们用最简单的方法来生成一个MongoCursor对象:

$m = new Mongo();
$collection = $m->demoDb->demoCollection;
$cursor = $collection->find();

当我们调用 find 方法的时候,会生成一个 MongoCursor 对象,而这时候只是生成一个内存中的对象而已,并不会把我们的 find 查询发送到服务端,因为在生成 MongoCursor 对象后,我们还可能对它做一些其它操作,比如 sort,limit 等等。这就对查询条件进行了改变。

那什么时候 PHP 会对 MongoDB 发起 find 的网络请求呢,是在 MongoCursor 调用 getNext 方法的时候。比如我们在上面代码的基础上,再执行 sort 和 getNext 两个方法:

$cursor->sort( array( 'name' => 1 ) );
$result = $cursor->getNext();

这时候第二行代码就会触发 find 的网络请求,具体请求的内容如下图,下图是对这次请求的二进制协议进行解析后的数据结构展示:

从上面图中我们可以看到,Number to Return 字段是0,MongoDB 协议里0表示不做限制,获取全部数据。所以这一次的 find 操作会把所有这个 collection 中的所有数据都拿到。而我们调用一次 getNext 实际上只拿到一条数据。那是不是说我们每调一次 getNext,PHP 就会进行一次网络请求获取一条数据呢?结果当然是否定的,这样效率未免也太低了。那好,那是不是 PHP 在第一次调用 getNext 就把所有数据拿回来,存在内存中,然后后续的 getNext 调用都在本地内存里取就行了呢?结果还是否定的,这样数据量大点 PHP 就容易被暴菊了吧。

所以事实上是怎么做的呢?我们来看下面一张图:

图上的 Number Returned 的值是101,也就是说 MongoDB 给我们返回了101条数据,这个101实际上就是服务器默认的 batchSize 大小。也就是说在没有指定返回多少条的情况下,会默认返回101条数据。这101条数据会存在 PHP 的内存中,这样后续的100次 getNext 调用,都不会再进行网络请求,而是直接从内存中返回数据。

如果我们在上面的 getNext 后再进行下面的调用。

// skip the other 100 docs
for ($i = 0; $i < 100; $i++) { $cursor->getNext(); }
// request document 102:
$result = $cursor->getNext();

上面先循环调用了100次 getNext,内存中的101项数据就都已经被取光了,然后当我们再次调用 getNext 去获取第102条数据的时候,PHP 内存中已经没有数据可以提供了,这时候又会再发起一次向 MongoDB 服务器的请求,去获取更多的数据。客户端这次会发起如下请求:

这次我们看到,请求的码变成了 Get More。也就是在上次的基础上获取更多数据。这时候实际 MongoDB 不会再按一个特定的条数返回数据,而是按一个特定的大小,目前是4M,也就是说,这一次,MongoDB 会返回最多4M的数据。对上面的请求,MongoDB 的返回如下:

这次返回结果中,标识了是从第101条开始,共返回了34673条数据。大小是4194378,正好是4M。

设置batchSize

上面我们说了,MongoDB 默认的 batchSize 是101条,这个条数实际上我们可以通过客户端来设定的。在 PHP 中,通过 batchSize 函数来进行设置。比如我们用下面命令设定 batchSize 为25:

$cursor = $collection->find()->sort

阅读全文

MongoDB运行状态、性能监控,分析

mongo stat
 这篇文章的目的是让你知道怎么了解你正在运行的Mongdb是否健康。

mongostat详解

mongostat是mongdb自带的状态检测工具,在命令行下使用。它会间隔固定时间获取mongodb的当前运行状态,并输出。如果你发现数据库突然变慢或者有其他问题的话,你第一手的操作就考虑采用mongostat来查看mongo的状态。

它的输出有以下几列:

  • inserts/s 每秒插入次数
  • query/s 每秒查询次数
  • update/s 每秒更新次数
  • delete/s 每秒删除次数
  • getmore/s 每秒执行getmore次数
  • command/s 每秒的命令数,比以上插入、查找、更新、删除的综合还多,还统计了别的命令
  • flushs/s 每秒执行fsync将数据写入硬盘的次数。
  • mapped/s 所有的被mmap的数据量,单位是MB,
  • vsize 虚拟内存使用量,单位MB
  • res 物理内存使用量,单位MB
  • faults/s 每秒访问失败数(只有Linux有),数据被交换出物理内存,放到swap。不要超过100,否则就是机器内存太小,造成频繁swap写入。此时要升级内存或者扩展
  • locked % 被锁的时间百分比,尽量控制在50%以下吧
  • idx miss % 索引不命中所占百分比。如果太高的话就要考虑索引是不是少了
  • q t|r|w 当Mongodb接收到太多的命令而数据库被锁住无法执行完成,它会将命令加入队列。这一栏显示了总共、读、写3个队列的长度,都为0的话表示mongo毫无压力。高并发时,一般队列值会升高。
  • conn 当前连接数
  • time 时间戳

使用profiler

阅读全文

深入浅出Symfony2 – 结合MongoDB开发LBS应用

1
简介 随着近几年各类移动终端的迅速普及,基于地理位置的服务(LBS)和相关应用也越来越多,而支撑这些应用的最基础技术之一,就是基于地理位置信息的处理。我所在的项目也正从事相关系统的开发,我们使用的是Symfony2+Doctrine2 ODM+MongoDB的组合。 我们将这些技术要点整理成文,希望能够通过本文的介绍和案例,详细解释如何使用MongoDB进行地理位置信息的查询和处理。在文章的开头,我们也会先介绍一下业界通常用来处理地理位置信息的一些方案并进行比较,让读者逐步了解使用MongoDB查询及处理地理位置信息的优势。 本文使用了Symfony2和Doctrine2作为Web应用的开发框架,对于想了解Symfony2的数据库操作的读者来说阅读本文也可以了解和掌握相关的技术和使用方法。 1. LBS类应用特点 不管是什么LBS应用,一个共同的特点就是:他们的数据都或多或少包含了地理位置信息。而如何对这些信息进行查询、处理、分析,也就成为了支撑LBS应用的最基础也是最关键的技术问题。 而由于地理位置信息的特殊性,在开发中经常会有比较难以处理的问题出现,比如:由于用户所在位置的不固定性,用户可能会在很小范围内移动,而此时经纬度值也会随之变化;甚至在同一个位置,通过GPS设备获取到的位置信息也可能不一样。所以如果通过经纬度去获取周边信息时,就很难像传统数据库那样做查询并进行缓存。 对于这个问题,有读者可能会说有别的处理方案,没错,比如只按经纬度固定的几位小数点做索引,比如按矩阵将用户划分到某固定小范围的区域(可以参考后文将会提到的geohash)等方式,虽然可以绕个弯子解决,但或多或少操作起来比较麻烦,也会牺牲一些精度,甚至无法做到性能的最优化,所以不能算作是最佳的解决办法。 而最近几年,直接支持地理位置操作的数据库层出不穷,其操作友好、性能高的特性也开始被我们慢慢重视起来,其中的佼佼者当属MongoDB。 MongoDB在地理位置信息的处理上有什么优势?下面我们通过一个简单的案例来对比一下各种技术方案之间进行进行地理位置信息处理的差异。 2. 几个地理位置信息处理方案的对比和分析 1. 确定功能需求 对于任何LBS应用来说,让用户寻找周围的好友可能都是一个必不可少的功能,我们就以这个功能为例,来看看各种处理方案之间的差异和区别。 我们假设有如下功能需求: 显示我附近的人 由近到远排序 显示距离 2. 可能的技术方案 排除一些不通用和难以实现的技术,我们罗列出以下几种方案: 基于MySQL数据库 采用GeoHash索引,基于MySQL MySQL空间存储(MySQL Spatial Extensions) 使用MongoDB存储地理位置信息 我们一个个来分析这几种方案。 方案1:基于MySQL数据库 MySQL的使用非常简单。对于大部分已经使用MySQL的网站来说,使用这种方案没有任何迁移和部署成本。 而在MySQL中查询“最近的人”也仅需一条SQL即可, SELECT id, ( 6371 * acos( cos( radians(37) ) * cos( radians( lat ) ) * cos( radians ( lng ) – radians(-122) ) + sin( radians(37) ) * sin( radians( lat ) ) ) ) AS distance  FROM places HAVING distance < 25 ORDER BY distance LIMIT 0 , 100; 注:这条SQL查询的是在lat,lng这个坐标附近的目标,并且按距离正序排列,SQL中的distance单位为公里。 但使用SQL语句进行查询的缺点也显而易见,每条SQL的计算量都会非常大,性能将会是严重的问题。 先别放弃,我们尝试对这条SQL做一些优化。 可以将圆形区域抽象为正方形,如下图 根据维基百科上的球面计算公式,可以根据圆心坐标计算出正方形四个点的坐标。 然后,查询这个正方形内的目标点。 SQL语句可以简化如下: SELECT * FROM places WHERE ((lat BETWEEN ? AND ?) AND (lng BETWEEN ? AND ?)) 这样优化后,虽然数据不完全精确,但性能提升很明显,并且可以通过给lat lng字段做索引的方式进一步加快这条SQL的查询速度。对精度有要求的应用也可以在这个结果上再进行计算,排除那些在方块范围内但不在原型范围内的数据,已达到对精度的要求。 可是这样查询出来的结果,是没有排序的,除非再进行一些SQL计算。但那又会在查询的过程中产生临时表排序,可能会造成性能问题。 方案2:GeoHash索引,基于MySQL GeoHash是一种地址编码,通过切分地图区域为小方块(切分次数越多,精度越高),它能把二维的经纬度编码成一维的字符串。也就是说,理论上geohash字符串表示的并不是一个点,而是一个矩形区域,只要矩形区域足够小,达到所需精度即可。(其实MongoDB的索引也是基于geohash) 如:wtw3ued9m就是目前我所在的位置,降低一些精度,就会是wtw3ued,再降低一些精度,就会是wtw3u。(点击链接查看坐标编码对应Google地图的位置) 所以这样一来,我们就可以在MySQL中用LIKE ‘wtw3u%’来限定区域范围查询目标点,并且可以对结果集做缓存。更不会因为微小的经纬度变化而无法用上数据库的Query Cache。 这种方案的优点显而易见,仅用一个字符串保存经纬度信息,并且精度由字符串从头到尾的长度决定,可以方便索引。 但这种方案的缺点是:从geohash的编码算法中可以看出,靠近每个方块边界两侧的点虽然十分接近,但所属的编码会完全不同。实际应用中,虽然可以通过去搜索环绕当前方块周围的8个方块来解决该问题,但一下子将原来只需要1次SQL查询变成了需要查询9次,这样不仅增大了查询量,也将原本简单的方案复杂化了。 除此之外,这个方案也无法直接得到距离,需要程序协助进行后续的排序计算。 方案3:MySQL空间存储 MySQL的空间扩展(MySQL Spatial Extensions),它允许在MySQL中直接处理、保存和分析地理位置相关的信息,看起来这是使用MySQL处理地理位置信息的“官方解决方案”。但恰恰很可惜的是:它却不支持某些最基本的地理位置操作,比如查询在半径范围内的所有数据。它甚至连两坐标点之间的距离计算方法都没有(MySQL Spatial的distance方法在5.*版本中不支持) 官方指南的做法是这样的: GLength(LineStringFromWKB(LineString(point1, point2))) 这条语句的处理逻辑是先通过两个点产生一个LineString的类型的数据,然后调用GLength得到这个LineString的实际长度。 这么做虽然有些复杂,貌似也解决了距离计算的问题,但读者需要注意的是:这种方法计算的是欧式空间的距离,简单来说,它给出的结果是两个点在三维空间中的直线距离,不是飞机在地球上飞的那条轨迹,而是笔直穿过地球的那条直线。 所以如果你的地理位置信息是用经纬度进行存储的,你就无法简单的直接使用这种方式进行距离计算。 方案4:使用MongoDB存储地理位置信息 MongoDB原生支持地理位置索引,可以直接用于位置距离计算和查询。 另外,它也是如今最流行的NoSQL数据库之一,除了能够很好地支持地理位置计算之外,还拥有诸如面向集合存储、模式自由、高性能、支持复杂查询、支持完全索引等等特性。 对于我们的需求,在MongoDB只需一个命令即可得到所需要的结果: db.runCommand( { geoNear: “places”, near: [ 121.4905, 31.2646 ], num:100 }) 查询结果默认将会由近到远排序,而且查询结果也包含目标点对象、距离目标点的距离等信息。 由于geoNear是MongoDB原生支持的查询函数,所以性能上也做到了高度的优化,完全可以应付生产环境的压力。 方案总结 基于MongoDB做附近查询是很方便的一件事情。 MongoDB在地理位置信息方面的表现远远不限于此,它还支持更多更加方便的功能,如范围查询、距离自动计算等。 接下来,我们结合Symfony2来详细地演示一些使用MongoDB进行地理位置信息处理的例子。 3. 结合Symfony2演示 运行环境 参考环境:Nginx1.2 + PHP5.4 + MongoDB2.4.3 + Symfony2.1 建立coordinate和places两个document文件,前者是作为places内的一个embed字段. 为方便演示效果,这里同时设置了两个索引 2d 和 2dsphere     Document/Coordinate.php     /**      * @MongoDB\EmbeddedDocument      */     class Coordinate {         /**         * @MongoDB\Field(type=”float”)         */         public $longitude;         /**         * @MongoDB\Field(type=”float”)         */         public $latitude;         …     }     Document/Place.php     /**     * @MongoDB\Document(collection=”places”)     * @MongoDB\ChangeTrackingPolicy(“DEFERRED_EXPLICIT”)     * @MongoDB\Indexes({     *   @MongoDB\Index(keys={“coordinate”=”2d”}),     *   @MongoDB\Index(keys={“coordinate”=”2dsphere”})     * })     */     class Place     {         /**         *         * @MongoDB\Id(strategy=”INCREMENT”)         */         protected $id;         /**         * @MongoDB\Field(type=”string”)         */         protected $title;         /**         * @MongoDB\Field(type=”string”)         */         protected $address;         /**         * @MongoDB\EmbedOne(targetDocument=”HenterGEO\GEOBundle\Document\Coordinate”)         */         protected $coordinate;         /**         * @MongoDB\Distance         */         public $distance;         …     } 坐标保存以longitude, latitude这个顺序(没有明确的限制和区别,但我们在此遵循官方的推荐)。 另外,为直观显示查询效果,默认使用百度地图标记查询数据。 程序说明 我们用到的代码包是doctrine/mongodb-odm-bundle(下文称ODM),这个代码包提供了在Symfony2环境下的MongoDB数据库支持,使用这个代码包,可以让我们更加方便的在Symfony2环境下操作MongoDB数据库。。 ODM封装了MongoDB中常用的一些地理位置函数,如周边搜索和范围搜索。 ODM中的操作默认距离单位是度,只有geoSphere支持弧度单位(必须在参数中指定spherical(true)) 4. MongoDB的地理位置查询 注意事项 下文大多数直接对MongoDB的数据库操作将使用Mongo Shell进行演示。在演示网站页面和功能时,将结合Symfony2、Doctrine-MongoDB进行演示。 本文演示所用的MongoDB版本为2.4.3,版本号比较新,所以某些查询方式在低版本里面并不支持。 以places这个collection为例,大部分例子都需要类似下面格式的测试数据支持: { “_id” : 2, “coordinate” : { “longitude” : 121.3449, “latitude” : 31.17528 }, “title” : “仅售75元,市场价210元的顶呱呱田鸡火锅3-4人套餐,无餐具费,冬日暖锅,欢迎品尝”, “address” : “闵行区航新路634号” } 地理位置索引: MongoDB地理位置索引常用的有两种。 2d 平面坐标索引,适用于基于平面的坐标计算。也支持球面距离计算,不过官方推荐使用2dsphere索引。 2dsphere 几何球体索引,适用于球面几何运算 关于两个坐标之间的距离,官方推荐2dsphere: MongoDB supports rudimentary spherical queries on flat 2d indexes for legacy reasons. In general, spherical calculations should use a 2dsphere index, as described in 2dsphere Indexes. 不过,只要坐标跨度不太大(比如几百几千公里),这两个索引计算出的距离相差几乎可以忽略不计。 建立索引: > db.places.ensureIndex({‘coordinate’:’2d’}) > db.places.ensureIndex({‘coordinate’:’2dsphere’}) 查询方式: 查询方式分三种情况: Inclusion。范围查询,如百度地图“视野内搜索”。 Inetersection。交集查询。不常用。 Proximity。周边查询,如“附近500内的餐厅”。 而查询坐标参数则分两种: 坐标对(经纬度)根据查询命令的不同,$maxDistance距离单位可能是 弧度 和 平面单位(经纬度的“度”):   db.<collection>.find( { <location field> :                    { $nearSphere: [ <x> , <y> ] ,                      $maxDistance: <distance in radians>                 } } ) GeoJson $maxDistance距离单位默认为米:   db.<collection>.find( { <location field> :                    { $nearSphere :                      { $geometry :                         { type : “Point” ,                           coordinates : [ <longitude> , <latitude> ] } ,                        $maxDistance : <distance in meters>              } } } ) 案例A:附近的人 查询当前坐标附近的目标,由近到远排列。 可以通过$near或$nearSphere,这两个方法类似,但默认情况下所用到的索引和距离单位不同。 查询方式: > db.places.find({‘coordinate’:{$near: [121.4905, 31.2646]}}) > db.places.find({‘coordinate’:{$nearSphere: [121.4905, 31.2646]}}) 查询结果: {     “_id” : 115,     “coordinate” : {         “longitude” : 121.4915,         “latitude” : 31.25933     },     “title” : “仅售148元,市场价298元的星程上服假日酒店全日房一间入住一天, 节假日通用,精致生活,品质享受”,     “address” : “虹口区天水路90号” } …(100条) 上述查询坐标[121.4905, 31.2646]附近的100个点,从最近到最远排序。 默认返回100条数据,也可以用limit()指定结果数量,如 > db.places.find({‘coordinate’:{$near: [121.4905, 31.2646]}}).limit(2) 指定最大距离 $maxDistance > db.places.find({‘coordinate’:{$near: [121.4905, 31.2646], $maxDistance:2}}) 结合Symfony2进行演示: 这里用near,默认以度为单位,公里数除以111(关于该距离单位后文有详细解释)。 /**  * @Route(“/near”, name=”near”)  * @Template()  */ public function nearAction(){     $longitude = (float)$this->getRequest()->get(‘lon’,121.4905);     $latitude = (float)$this->getRequest()->get(‘lat’,31.2646);     //2km     $max = (float)$this->getRequest()->get(‘max’, 2);     $places = $this->getPlaceRepository()->createQueryBuilder()         ->field(‘coordinate’)->near($longitude, $latitude)         ->maxDistance($max/111)         ->getQuery()->toarray();     return compact(‘places’,’max’,’longitude’,’latitude’); } 通过 domain.dev/near 访问,效果如下:   longitude: xxx, latitude: xxx为当前位置,我们在地图上显示了周边100条目标记录 案例B:区域内搜索 MongoDB中的范围搜索(Inclusion)主要用$geoWithin这个命令,它又细分为3种不同类型,如下: $box 矩形 $center 圆(平面),$centerSphere圆(球面) $polygon 多边形 $center和$centerSphere在小范围内的应用几乎没差别(除非这个圆半径几百上千公里)。 下面我们介绍一下这三种查询的案例。 矩形区域 这个比较常用,比如百度地图的视野内搜索(矩形)、或搜狗地图的“拉框搜索” 定义一个矩形范围,我们需要指定两个坐标,在MongoDB的查询方式如下: > db.places.find(     {         coordinate : {             $geoWithin : {                 $box :[ [ 121.44, 31.25 ] , [ 121.5005, 31.2846 ] ]             }         }     } ) 查询结果: {     “_id” : 90472,     “title” : “【鲁迅公园】仅售99元!酒店门市价288元的上海虹元商务宾馆客房一间入住一天(需持本人有效 身份证件办理登记):大床房/标准房(2选1)!不含早餐!不涉外!2012年9月29日-10月6日 不可使用拉手券!可延迟退房至14:00!”,     “address” : “上海市虹口区柳营路8号”,     “coordinate” : {         “longitude” : 121.47,         “latitude” : 31.27145     } } … … Symfony2演示代码: 指定两个坐标点 /**  * @Route(“/box”, name=”box”)  * @Template()  */ public function boxAction(){     $request = $this->getRequest();     $longitude = (float)$request->get(‘lon’,121.462035);     $latitude = (float)$request->get(‘lat’,31.237641);     $longitude2 = (float)$request->get(‘lon2’,121.522098);     $latitude2 = (float)$request->get(‘lat2’,31.215284);     $places = $this->getPlaceRepository()->createQueryBuilder()         ->field(‘coordinate’)->withinBox($longitude, $latitude, $longitude2, $latitude2)         ->getQuery()->toarray();     return compact(‘places’,’longitude’,’latitude’, ‘longitude2’, ‘latitude2’); } 通过 domain.dev/box 访问,效果如下: 圆形区域 应用场景有:地图搜索租房信息 查询以某坐标为圆心,指定半径的圆内的数据。 前面已提到,圆形区域搜索分为$center和$centerSphere这两种类型,它们的区别主要在于支持的索引和默认距离单位不同。 2d索引能同时支持$center和$centerSphere,2dsphere索引支持$centerSphere。关于距离单位,$center默认是度,$centerSphere默认距离是弧度。 查询方式如下: > db.places.find({‘coordinate’:{$geoWithin:{$centerSphere:[ [121.4905, 31.2646] , 0.6/111] }}}) 或 > db.places.find({‘coordinate’:{$geoWithin:{$centerSphere:[ [121.4905, 31.2646] , 0.6/6371] }}}) 查询结果 {     “_id” : 115,     “coordinate” : {         “longitude” : 121.4915,         “latitude” : 31.25933     },     “title” : “仅售148元,市场价298元的星程上服假日酒店全日房一间入住一天,节假日通用, 精致生活,品质享受”,     “address” : “虹口区天水路90号” } … Symfony2演示代码: 指定圆心坐标和半径 /**  * @Route(“/center”, name=”center”)  * @Template()  */ public function centerAction(){     $request = $this->getRequest();     $longitude = (float)$request->get(‘lon’,121.4905);     $latitude = (float)$request->get(‘lat’,31.2646);     //10km     $max = (float)$request->get(‘max’, 10);     $places = $this->getPlaceRepository()->createQueryBuilder()         ->field(‘coordinate’)->withinCenter($longitude, $latitude, $max/111)         ->getQuery()->toarray();     return compact(‘places’,’max’,’longitude’,’latitude’); } 通过 domain.dev/center 访问,效果如下: 以longitude: xxx,latitude: xxx为中心点,半径10km的圆内 多边形 复杂区域内的查询,这个应用场景比较少见。指定至少3个坐标点,查询方式如下(五边形): > db.places.find( { coordinate : { $geoWithin : { $polygon : [     [121.45183 , 31.243816] ,     [121.533181, 31.24344] ,     [121.535049, 31.208983] ,     [121.448955, 31.214913] ,     [121.440619, 31.228748] ] } } } ) 查询结果 {     “_id” : 90078,     “title” : “仅售9.9元,市场价38元的燕太太燕窝单人甜品餐,用耐心守候一盅炖品,用爱滋补一生情谊”,     “address” : “河南南路489号香港名都购物广场1F125燕太太燕窝”,     “coordinate” : {         “longitude” : 121.48912,         “latitude” : 31.22355     } } … Symfony2演示代码(这里为方便,直接写死了5个坐标点): /**  * @Route(“/polygon”, name=”polygon”)  * @Template()  */ public function polygonAction(){     $points = [];     $points[] = [121.45183,31.243816];     $points[] = [121.533181,31.24344];     $points[] = [121.535049,31.208983];     $points[] = [121.448955,31.214913];     $points[] = [121.440619,31.228748];     $sumlon = $sumlat = 0;     foreach($points as $p){         $sumlon += $p[0];         $sumlat += $p[1];     }     $center = [$sumlon/count($points), $sumlat/count($points)];     $places = $this->getPlaceRepository()->createQueryBuilder()         ->field(‘coordinate’)->withinPolygon($points[0], $points[1], $points[2], $points[3], $points[4])         ->getQuery()->toarray();     return compact(‘places’,’points’, ‘center’); } 通过 domain.dev/polygon 访问,效果如下: 案例C:附近的餐厅 我们假设需要以当前坐标为原点,查询附近指定范围内的餐厅,并直接显示距离。 这个需求用前面提到的$near是可以实现的,但是距离需要二次计算。这里我们用$geoNear这个命令查询。 $geoNear与$near功能类似,但提供更多功能和返回更多信息,官方文档是这么解释的: The geoNear command provides an alternative to the $near operator. In addition to the functionality of $near, geoNear returns additional diagnostic information. 查询方式如下(关于下面的示例用到了distanceMultipler函数,后文会详细解释): > db.runCommand( { geoNear: “places”, near: [ 121.4905, 31.2646 ], spherical: true,  maxDistance:1/6371, num:2 }) {     “ns” : “mongo_test.places”,     “near” : “1110001100111100001011010110010111001000110011111101”,     “results” : [         {             “dis” : 0.00009318095248858048,             “obj” : {                 “_id” : 115,                 “coordinate” : {                     “longitude” : 121.4915,                     “latitude” : 31.25933                 },                 “title” : “仅售148元,市场价298元的星程上服假日酒店全日房一间入住一天, 节假日通用,精致生活,品质享受”,                 “address” : “虹口区天水路90号”             }         },         {             “dis” : 0.00010610660597329082,             “obj” : {                 “_id” : 465,                 “coordinate” : {                     “longitude” : 121.48406,                     “latitude” : 31.26202                 },                 “title” : “【四川北路】热烈庆祝康骏会馆成立8周年!仅售169元!市场价399元的 康骏会馆四川北路一店(仅限3星级技师)全身精油按摩一人次!全程约90分钟! 男女不限!仅限四川北路一店使用,非本市所有门店通用!拉手券消费仅限每日19:00前! 健康有道,骏越万里!”,                 “address” : “虹口区四川北路1896号-1904号201室”             }         }     ],     “stats” : {         “time” : 0,         “btreelocs” : 0,         “nscanned” : 18,         “objectsLoaded” : 12,         “avgDistance” : 0.00009964377923093564,         “maxDistance” : 0.0001064199324957278     },     “ok” : 1 } 可以看到返回了很多详细信息,如查询时间、返回数量、最大距离、平均距离等。 另外,results里面直接返回了距离目标点的距离dis。 Symfony2演示代码: /**  * @Route(“/distance”, name=”distance”)  * @Template()  */ public function distanceAction(){     $longitude = (float)$this->getRequest()->get(‘lon’,121.4905);     $latitude = (float)$this->getRequest()->get(‘lat’,31.2646);     //2km     $max = (float)$this->getRequest()->get(‘max’, 2);     $places = $this->getPlaceRepository()->createQueryBuilder()         ->field(‘coordinate’)         ->geoNear($longitude, $latitude)         ->spherical(true)         ->distanceMultiplier(6371)         ->maxDistance($max/6371)         ->limit(100)         ->getQuery()         ->execute()         ->toArray();     return compact(‘places’,’longitude’, ‘latitude’, ‘max’); } 通过 domian.dev/distance 访问,效果如下: 距离xxx米 小结 前面演示的查询代码中,坐标都是按照 longitude, latitude这个顺序的。 这个是官方建议的坐标顺序,但是网上很多文档是相反的顺序,经测试发现,只要查询时指定的坐标顺序与数据库内的坐标顺序一致,出来的结果就是正确的,没有特定的先后顺序之分。 但鉴于官方文档的推荐,我在此还是建议大家按照官方推荐的顺序。 案例A的$near和案例B的$center从需求上看差不多,但是$center或$centerSphere是属于$geoWithin的类型,$near方法查询后会对结果集对距离进行排序,而$geoWithin是无序的。 常用的查询方式已经介绍完了,不常用的比如geoIntersect查询,这里不做介绍,但是已经包含在开源的演示程序里了,有兴趣的读者可以自行测试研究。 下面介绍前文提到的距离单位等问题。 5. 需要注意的问题 索引 $near命令必须要求有索引。 $geoWithin可以无需索引,但是建议还是建立索引以提升性能。 距离单位 MongoDB查询地理位置默认有3种距离单位: 米(meters) 平面单位(flat units,可以理解为经纬度的“一度”) 弧度(radians)。 通过GeoJSON格式查询,单位默认是米,通过其它方式则比较混乱,下面详细解释一下。 下面的查询语句指定距离内的目标: > db.places.find({‘coordinate’:{$near: [121.4905, 31.2646], $maxDistance:2}}) 现在$maxDistance参数是2,但是如果我要查询如“附近500米内的餐厅”这样的需求,这个参数应该是多少? 关于距离计算,MongoDB的官方文档仅仅提到了弧度计算,未说明水平单位(度)计算。 关于弧度计算,官方文档的说明是: To convert: distance to radians: divide the distance by the radius of the sphere (e.g. the Earth) in the same units as the distance measurement. radians to distance: multiply the radian measure by the radius of the sphere (e.g. the Earth) in the units system that you want to convert the distance to. The radius of the Earth is approximately 3,959 miles or 6,371 kilometers. 所以如果用弧度查询,则以公里数除以6371,如“附近500米的餐厅”: > db.runCommand( { geoNear: “places”, near: [ 121.4905, 31.2646 ], spherical: true,  $maxDistance: 0.5/6371 }) 那如果不用弧度,以水平单位(度)查询时,距离单位如何处理? 答案是以公里数除以111(推荐值),原因如下: 经纬度的一度,分为经度一度和纬度一度。 地球不同纬度之间的距离是一样的,地球子午线(南极到北极的连线)长度39940.67公里,纬度一度大约110.9公里 但是不同纬度的经度一度对应的长度是不一样的: 在地球赤道,一圈大约为40075KM,除以360度,每一个经度大概是:40075/360=111.32KM 上海,大概在北纬31度,对应一个经度的长度是:40075*sin(90-31)/360=95.41KM 北京在北纬40度,对应的是85KM 前面提到的参数111,这个值只是估算,并不完全准确,任意两点之间的距离,平均纬度越大,这个参数则误差越大。详细原因可以参考wiki上的解释:http://en.wikipedia.org/wiki/Latitude 但是,即便如此,“度”这个单位只用于平面,由于地球是圆的,在大范围使用时会有误差。 官方建议使用sphere查询方式,也就是说距离单位用弧度。 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. $geoNear返回结果集中的dis,如果指定了spherical为true, dis的值为弧度,不指定则为度。 指定 spherical为true,结果中的dis需要乘以6371换算为km: > db.runCommand( { geoNear: “places”, near: [ 121.4905, 31.2646 ], spherical: true, num:1 }) {     “ns” : “mongo_test.places”,     “near” : “1110001100111100001011010110010111001000110011111101”,     “results” : [         {             “dis” : 0.00009318095248858048,             “obj” : {                 “_id” : 115,                 “coordinate” : {                     “longitude” : 121.4915,                     “latitude” : 31.25933                 },                 “title” : “仅售148元,市场价298元的星程上服假日酒店全日房一间入住一天,节假日通用, 精致生活,品质享受”,                 “address” : “虹口区天水路90号”             }         }     ],     “stats” : {         “time” : 0,         “btreelocs” : 0,         “nscanned” : 18,         “objectsLoaded” : 12,         “avgDistance” : 0.00009964377923093564,         “maxDistance” : 0.0001064199324957278     },     “ok” : 1 } 不指定sphericial,结果中的dis需要乘以111换算为km: > db.runCommand( { geoNear: “places”, near: [ 121.4905, 31.2646 ], num:1 }) {     “ns” : “mongo_test.places”,     “near” : “1110001100111100001011010110010111001000110011111101”,     “results” : [         {             “dis” : 0.005364037658335473,             “obj” : {                 “_id” : 115,                 “coordinate” : {                     “longitude” : 121.4915,                     “latitude” : 31.25933                 },                 “title” : “仅售148元,市场价298元的星程上服假日酒店全日房一间入住一天,节假日通用, 精致生活,品质享受”,                 “address” : “虹口区天水路90号”             }         }     ],     “stats” : {         “time” : 0,         “btreelocs” : 0,         “nscanned” : 18,         “objectsLoaded” : 12,         “avgDistance” : 0.006150808243357531,         “maxDistance” : 0.00695541352612983     },     “ok” : 1 } 说到这里读者是不是已经有点迷糊了?没关系,在开发中其实你并不需要去知道各种距离单位的历史和使用它的原因,我在此为你总结了一张表,大部分常用的函数和所使用的距离单位都已经被我整理了出来,你只需要参考表上所列的距离单位直接使用即可。 查询命令 距离单位 说明 $near 度 官方文档上关于这一点是错的 $nearSphere 弧度 $center 度 $centerSphere 弧度 $polygon 度 $geoNear 度或弧度 指定参数spherical为true则为弧度,否则为度 如果坐标以GeoJSON格式,则单位都为米。 当然如果你的操作比较复杂,或者希望知道更加详细的对照关系,也可以参考官方的这个更详细的对比表格:http://docs.mongodb.org/manual/reference/operator/query-geospatial/ 单位自动换算 如上面两个geoNear示例,结果中的dis,前文已经提过这是与目标点的距离,但是这个距离单位是跟查询单位一致的,需要二次计算,不太方便。 而其实可以直接在查询时指定 distanceMultiplier ,它会将这个参数乘以距离返回,如指定为6371,返回的就是公里数。 > db.runCommand({ geoNear : “places”, near : [121.4905, 31.2646], spherical : true,  maxDistance : 1/6371, distanceMultiplier: 6371}) {     “ns” : “mongo_test.places”,     “near” : “1110001100111100001011010110010111001000110011111101”,     “results” : [         {             “dis” : 0.5936558483047463,             “obj” : {                 “_id” : 115,                 “coordinate” : {                     “longitude” : 121.4915,                     “latitude” : 31.25933                 },                 “title” : “仅售148元,市场价298元的星程上服假日酒店全日房一间入住一天,节假日通用, 精致生活,品质享受”,                 “address” : “虹口区天水路90号”             }         },         …         …     ],     “stats” : {         “time” : 0,         “btreelocs” : 0,         “nscanned” : 15,         “objectsLoaded” : 9,         “avgDistance” : 0.6348305174802911,         “maxDistance” : 0.0001064199324957278     },     “ok” : 1 } 注意上面的结果中dis的值,已经是km单位的了。 结语 通过前面的案例演示,相信大家对MongoDB的地理位置特性已经比较了解。 MongoDB还有很多很酷的功能,地址位置支持仅是其中一项。希望以后能有机会为各位读者介绍如何结合Symfony2使用MongoDB进行应用开发的更多案例。 文中的演示程序已经发布在了Github上,地址是https://github.com/henter/HenterGEO,读者可以直接使用。 参考: http://docs.mongodb.org/manual/ https://wiki.10gen.com/pages/viewpage.action?pageId=21268367&navigatingVersions=true http://en.wikipedia.org/wiki/Radian

<a href="http://www.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL

阅读全文

MongoDB索引小结

官方文档索引部分的地址:

http://docs.mongodb.org/manual/indexes/

1.创建索引

1)创建单个索引

db.collection.ensureIndex({a:1})

在a字段上创建一个升序的索引(对于单个字段的索引,升序或是降序都一样)。

2)创建复合索引

db.collection.ensureIndex({a:1,b:-1})

3)创建稀疏索引

db.collection.ensureIndex({a:1},{sparse:true})

索引中将不包含没有a字段的文档。

阅读全文

Mongodb 忘记密码的解决办法

这篇文章主要介绍了Mongodb数据库忘记密码的解决办法,需要的朋友可以参考下。

下午刚设置的密码,当时忘记保存,晚上去吃了个晚饭回来就忘记了。研究了一会发现也不难,按照以下步骤操作即可

操作步骤: vim /etc/mongodb.conf          # 修改 mongodb 配置,将 auth = true 注释掉,或者改成 false<br

阅读全文

Mongo db 与mysql 语法比较

mongodb与mysql命令对比

传统的关系数据库一般由数据库(database)、表(table)、记录(record)三个层次概念组成,MongoDB是由数据库(database)、集合(collection)、文档对象(document)三个层次组成。MongoDB对于关系型数据库里的表,但是集合中没有列、行和关系概念,这体现了模式自由的特点。

MySQL

MongoDB

说明

mysqld mongod 服务器守护进程
mysql mongo 客户端工具
mysqldump mongodump 逻辑备份工具
mysql mongorestore 逻辑恢复工具
db.repairDatabase() 修复数据库
mysqldump mongoexport 数据导出工具
source mongoimport 数据导入工具
grant * privileges on *.* to … Db.addUser()

Db.auth()

新建用户并权限
show databases show dbs 显示库列表
Show tables Show collections 显示表列表
Show slave status Rs.status 查询主从状态
Create table users(a int, b int) db.createCollection(“mycoll”, {capped:true,

size:100000}) 另:可隐式创建表。

创建表
Create INDEX idxname ON users(name) db.users.ensureIndex({name:1}) 创建索引
Create INDEX idxname ON users(name,ts DESC) db.users.ensureIndex({name:1,ts:-1}) 创建索引
Insert into users values(1, 1) db.users.insert({a:1, b:1}) 插入记录
Select a, b from users db.users.find({},{a:1, b:1}) 查询表
Select * from users db.users.find() 查询表
Select * from users where age=33 db.users.find({age:33}) 条件查询
Select a, b from users where age=33 db.users.find({age:33},{a:1, b:1}) 条件查询
select * from users where age<33 db.users.find({‘age’:{$lt:33}}) 条件查询
select * from users where age>33 and age<=40 db.users.find({‘age’:{$gt:33,$lte:40}}) 条件查询
select * from users where a=1 and b=’q’ db.users.find({a:1,b:’q’}) 条件查询
select * from users where a=1 or b=2 db.users.find( { $or : [ { a : 1 } , { b : 2 } ] } ) 条件查询
select * from users limit 1 db.users.findOne() 条件查询
select * from users where name like “%Joe%” db.users.find({name:/Joe/}) 模糊查询
select * from users where name like “Joe%” db.users.find({name:/^Joe/}) 模糊查询
select count(1) from users Db.users.count() 获取表记录数
select count(1) from users where age>30 db.users.find({age: {‘$gt’: 30}}).count() 获取表记录数
select DISTINCT last_name from users db.users.distinct(‘last_name’) 去掉重复值
select * from users ORDER BY name db.users.find().sort({name:-1}) 排序
select * from users ORDER BY name DESC db.users.find().sort({name:-1}) 排序
EXPLAIN select * from users where z=3 db.users.find({z:3}).explain() 获取存储路径
update users set a=1 where b=’q’ db.users.update({b:’q’}, {$set:{a:1}}, false, true) 更新记录
update users set a=a+2 where b=’q’ db.users.update({b:’q’}, {$inc:{a:2}}, false, true) 更新记录
delete from users where z=”abc” db.users.remove({z:’abc’}) 删除记录
db. users.remove() 删除所有的记录
drop database IF EXISTS test; use test

db.dropDatabase()

删除数据库
drop table IF EXISTS test; db.mytable.drop() 删除表/collection
db.addUser(‘test’, ’test’) 添加用户

readOnly–>false

db.addUser(‘test’, ’test’, true) 添加用户

readOnly–>true

db.addUser(“test”,”test222″) 更改密码
db.system.users.remove({user:”test”})

或者db.removeUser(‘test’)

删除用户
use admin 超级用户
db.auth(‘test’, ‘test’) 用户授权
db.system.users.find() 查看用户列表
show users 查看所有用户
db.printCollectionStats() 查看各collection的状态
db.printReplicationInfo() 查看主从复制状态
show profile 查看profiling
db.copyDatabase(‘mail_addr’,’mail_addr_tmp’) 拷贝数据库
db.users.dataSize() 查看collection数据的大小
db. users.totalIndexSize() 查询索引的大小

mongodb语法

阅读全文

mongodb使用elemmatch匹配数组内的组合元素

mongodb:     db.user_lives.find({“att_car”:{“$elemMatch”:{“s_type”:1,”pserid”:39}}}); php:     $criteria = new VMongoCriteria();     $criteria->addCond(“att_car”, “elemmatch”, array(“pserid”=>$pserid,”s_type”=>1));     $res = $ulObj->findAll($criteria); rockmongo:     “att_car”:{“$elemMatch”:{“s_type”:1,”pserid”:39}}