最近在学习MEAN,看的是Simon Helmes的Getting MEAN with Mongo, Express, ANgular, and Node这本书。其中Chapter 8 Adding Angular components to an Express application中涉及到了Mongoose
的geoNear
方法的使用。不过本身在按照做者的方法进行测试的时候,发现并不能输出想要的结果。经过相关研究找到了解决的方法,所以分享。javascript
做者在书中演示了经过浏览器的navigator.geolocation
发送经纬度坐标到API接口,接着后台使用Mongoose
的geoNear
方法,从数据库中将离目标坐标较近的数据推送出来。后台从Mongo
中取数的大体代码以下:java
/* GET list of locations */ module.exports.locationsListByDistance = function(req, res) { var lng = parseFloat(req.query.lng); var lat = parseFloat(req.query.lat); var maxDistance = parseFloat(req.query.maxDistance); var point = { type: "Point", coordinates: [lng, lat] }; var geoOptions = { spherical: true, maxDistance: theEarth.getRadsFromDistance(maxDistance), num: 10 }; if ((!lng && lng!==0) || (!lat && lat!==0) || ! maxDistance) { // ... } Loc.geoNear(point, geoOptions, function(err, results, stats) { // ... }); };
其中,做者的意思是maxDistance
数据是按照千米进行输入,而后转换为弧度,并把弧度做为参数传入geoNear
中。可是获得的结果,确实没有任何数据输出。node
通过查找后发现,Mongo中对此是以下的定义:git
2dsphere Indexgithub
If using a 2dsphere index, you can specify either a GeoJSON point or a legacy coordinate pair for the near value.
You must include spherical: true in the syntax.
With spherical: true, if you specify a GeoJSON point, MongoDB uses meters as the unit of measurement:mongodbdb.runCommand( { geoNear: <collection> , near: { type: "Point" , coordinates: [ <coordinates> ] } , spherical: true, ... } )With spherical: true, if you specify a legacy coordinate pair, MongoDB
uses radians as the unit of measurement:数据库db.runCommand( { geoNear: <collection> , near: [ <coordinates> ], spherical: true, ... } )
书中的源代码确实是GeoJSON
的格式,那为什么做者使用了弧度,而没有使用米呢?原来Mongoose
在3.9.5
版本才支持了Mongo
的这个设置。原文以下:express
3.9.5 / 2014-11-10
fixed; geoNear() no longer enforces legacy coordinate pairs - supports GeoJSON #1987 alabid浏览器
用多是做者在写书的时候,还用的OK,后来版本更新后,设置就失效了。app
所以,按照做者原来的思路,代码应该改成:
/* GET list of locations */ module.exports.locationsListByDistance = function(req, res) { // ... var geoOptions = { spherical: true, maxDistance: maxDistance * 1000, // <-- num: 10 }; // ... }; var buildLocationList = function(req, res, results, stats) { var locations = []; results.forEach(function(doc) { locations.push({ distance: doc.dis / 1000, // <-- // ... }); }); return locations; };