關於Mongoose的geoNear方法的使用

背景

最近在學習MEAN,看的是Simon HelmesGetting MEAN with Mongo, Express, ANgular, and Node這本書。其中Chapter 8 Adding Angular components to an Express application中涉及到了MongoosegeoNear方法的使用。不過本身在按照做者的方法進行測試的時候,發現並不能輸出想要的結果。經過相關研究找到了解決的方法,所以分享。javascript

問題

做者在書中演示了經過瀏覽器的navigator.geolocation發送經緯度座標到API接口,接着後臺使用MongoosegeoNear方法,從數據庫中將離目標座標較近的數據推送出來。後臺從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:mongodb

db.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的格式,那爲什麼做者使用了弧度,而沒有使用米呢?原來Mongoose3.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;
};
相關文章
相關標籤/搜索