{"id":185,"date":"2012-03-12T01:44:07","date_gmt":"2012-03-12T06:44:07","guid":{"rendered":"http:\/\/jamesroberts.name\/blog\/?p=185"},"modified":"2012-03-12T01:54:22","modified_gmt":"2012-03-12T06:54:22","slug":"mongodb-geospatial-query-example-in-php-with-distance-radius-in-miles-and-km","status":"publish","type":"post","link":"https:\/\/jamesroberts.name\/blog\/2012\/03\/12\/mongodb-geospatial-query-example-in-php-with-distance-radius-in-miles-and-km\/","title":{"rendered":"mongodb geospatial query example in php with distance radius in miles and km"},"content":{"rendered":"<p>Recently for a project involving 311 reports in New York city, I had to find a speedy way to query over 3 million records within a certain radius of a latitude longitude point.  Mysql is slow for this sort of thing, so I decided to try the data set out with Solr.  Solr worked great and was quite responsive, but required a massive amount of RAM on our server to hold the entire dataset.  This was &#8220;ok&#8221; for our current dataset, but what would happen when we started to expand beyond NYC? or if we kept adding on new 311 reports from NYC as they come in over time?  Solr was not scaleable.  Mongodb to the rescue!<\/p>\n<p>I wrote a simple import script in PHP to pull in the data we had already imported into mysql into mongodb.  The only special part of the data mirgation was the creation of a &#8220;lnglat&#8221; field in each imported document array which is a &#8220;2d index&#8221; in mongo.  You have to set a multivalued field with the lat\/lng values cast as floats in the document array, like this:<\/p>\n<pre lang=\"php\">$doc['lnglat'] = array('lng'=>(float)$row['lng'],'lat'=>(float)$row['lat']);<\/pre>\n<p>After the data has been imported to mongo, you create the 2d index on that multivalued field using the mongo command line, with this command:<\/p>\n<pre lang=\"javascript\">my_mongodb.my_collection.ensureIndex( { lnglat : \"2d\" } )<\/pre>\n<p>Once I had the data in mongo, I had a hard time finding good examples of Geospatial queries for mongodb in php.  I finally found this page:<br \/>\n<a href=\"http:\/\/comments.gmane.org\/gmane.comp.db.mongodb.user\/48789\" title=\"http:\/\/comments.gmane.org\/gmane.comp.db.mongodb.user\/48789\" target=\"_blank\">http:\/\/comments.gmane.org\/gmane.comp.db.mongodb.user\/48789<\/a><br \/>\nwhich solved all my problems and pointed out that when querying mongo with a lat\/lng point as an array, mongodb expects the lng to be first, not the lat. Durrrr&#8230;<\/p>\n<p>Anyway, here is the code snippit that I pulled from the <a href=\"http:\/\/comments.gmane.org\/gmane.comp.db.mongodb.user\/48789\" title=\"useful php example of geospatial queries using mongodb with distance radius\">url above<\/a> which was the first comprehensible example I found of php code for geospatial queries in with a distance radius specified:<\/p>\n<pre lang=\"php\">\r\npublic function getRadiusMiles($lng, $lat, $radius = 3) {\r\n\t$radiusOfEarth = 3956; \/\/avg radius of earth in miles\r\n\t$cursor = $this->collection->find(\r\n\t\tarray('lnglat' =>\r\n\t\t\tarray('$within' =>\r\n\t\t\t\tarray('$centerSphere' =>\r\n\t\t\t\t\tarray(\r\n\t\t\t\t\t\tarray(floatval($lng), floatval($lat)), $radius\/$radiusOfEarth\r\n\t\t\t\t\t)\r\n\t\t\t\t)\r\n\t\t\t)\r\n\t\t)\r\n\t);\r\n\t$resultset = $this->jsonCursor($cursor);\r\n\treturn json_encode($resultset);\r\n}\r\n\r\npublic function getRadiusKm($lng, $lat, $radius = 5) {\r\n\t$radiusOfEarth = 6378.1; \/\/avg radius of earth in km\r\n\t$cursor = $this->collection->find(\r\n\t\tarray('lnglat' =>\r\n\t\t\tarray('$within' =>\r\n\t\t\t\tarray('$centerSphere' =>\r\n\t\t\t\t\tarray(\r\n\t\t\t\t\t\tarray(floatval($lng), floatval($lat)), $radius\/$radiusOfEarth\r\n\t\t\t\t\t)\r\n\t\t\t\t)\r\n\t\t\t)\r\n\t\t)\r\n\t);\r\n\r\n\t$resultset = $this->jsonCursor($cursor);\r\n\treturn json_encode($resultset);\r\n}\r\n<\/pre>\n<p>Hopefully this saves someone some time!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Recently for a project involving 311 reports in New York city, I had to find a speedy way to query over 3 million records within a certain radius of a latitude longitude point. Mysql is slow for this sort of thing, so I decided to try the data set out with Solr. Solr worked great [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[41,10],"tags":[42,43,44,67,45,62],"_links":{"self":[{"href":"https:\/\/jamesroberts.name\/blog\/wp-json\/wp\/v2\/posts\/185"}],"collection":[{"href":"https:\/\/jamesroberts.name\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/jamesroberts.name\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/jamesroberts.name\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/jamesroberts.name\/blog\/wp-json\/wp\/v2\/comments?post=185"}],"version-history":[{"count":6,"href":"https:\/\/jamesroberts.name\/blog\/wp-json\/wp\/v2\/posts\/185\/revisions"}],"predecessor-version":[{"id":187,"href":"https:\/\/jamesroberts.name\/blog\/wp-json\/wp\/v2\/posts\/185\/revisions\/187"}],"wp:attachment":[{"href":"https:\/\/jamesroberts.name\/blog\/wp-json\/wp\/v2\/media?parent=185"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/jamesroberts.name\/blog\/wp-json\/wp\/v2\/categories?post=185"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/jamesroberts.name\/blog\/wp-json\/wp\/v2\/tags?post=185"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}