Mongoose/MongoDb getting error geoNear is not a fu

2020-07-08 07:23发布

This is my controller file locations.js

var mongoose = require('mongoose');
var Loc = mongoose.model('location');

module.exports.locationsListByDistance = function(req, res) {
  var lng = parseFloat(req.query.lng);
  var lat = parseFloat(req.query.lat);
  var point = {
    type: "Point",
    coordinates: [lng, lat]
  };
  var geoOptions = {
    spherical: true,
    maxDistance: 1000
  };

  Loc.geoNear(point, geoOptions, function (err, results, stats) {
    console.log(results);
  });
};

My model file locations.js

var mongoose = require('mongoose');

var reviewSchema = new mongoose.Schema({
    author: String,
    rating: {
        type: Number,
        required: true,
        min: 0,
        max: 5
    },
    reviewText: String,
    createdOn: {
        type: Date,
        "default": Date.now
    }
});

var openingTimeSchema = new mongoose.Schema({
    days: {
        type: String,
        required: true
    },
    opening: String,
    closing: String,
    closed: {
        type: Boolean,
        required: true
    }
});

var locationSchema = new mongoose.Schema({
    name: {
        type: String,
        required: true
    },
    address: String,
    rating: {
        type: Number,
        "default": 0,
        min: 0,
        max: 5
    },
    facilities: [String],
    // Always store coordinates longitude, latitude order.
    coords: {
        type: [Number],
        index: '2dsphere'
    },
    openingTimes: [openingTimeSchema],
    reviews: [reviewSchema]
});

mongoose.model('location', locationSchema, 'locations');

Whenever I run http://localhost:3000/api/locations?lng=-0.9690884&lat=51.455041 I get error geoNear is not a function

TypeError: Loc.geoNear is not a function at module.exports.locationsListByDistance (/home/shackers/Projects/mean/loc8r/app_api/controllers/locations.js:51:7) at Layer.handle [as handle_request] (/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/layer.js:95:5) at next (/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/route.js:137:13) at Route.dispatch (/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/route.js:112:3) at Layer.handle [as handle_request] (/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/layer.js:95:5) at /home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/index.js:281:22 at Function.process_params (/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/index.js:335:12) at next (/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/index.js:275:10) at Function.handle (/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/index.js:174:3) at router (/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/index.js:47:12) at Layer.handle [as handle_request] (/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/layer.js:95:5) at trim_prefix (/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/index.js:317:13) at /home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/index.js:284:7 at Function.process_params (/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/index.js:335:12) at next (/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/index.js:275:10) at /home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/index.js:635:15

This are versions of dependencies i am using:

  • node : 8.9.3 npm : 5.5.1 express : 4.15.5 mongoose : 5.0.0 mongoDb : 3.6.1

10条回答
倾城 Initia
2楼-- · 2020-07-08 07:37

Apparently I'm in the same book (Getting Mean, Manning) and running into roughly the same issues. This seems to work for me:

var mongoose = require('mongoose');
var Loc = mongoose.model('Location');

var sendJSONresponse = function(res, status, content) {
  res.status(status);
  res.json(content);
};

var theEarth = (function() {
  console.log('theEarth');
  var earthRadius = 6371; // km, miles is 3959

  var getDistanceFromRads = function(rads) {
    return parseFloat(rads * earthRadius);
  };

  var getRadsFromDistance = function(distance) {
    return parseFloat(distance / earthRadius);
  };

  return {
    getDistanceFromRads: getDistanceFromRads,
    getRadsFromDistance: getRadsFromDistance
  };
})();

/* GET list of locations */
module.exports.locationsListByDistance = function(req, res) {
  console.log('locationsListByDistance:');
  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]
  };
  console.log('point: ' + point)
  var geoOptions = {
    spherical: true,
    maxDistance: theEarth.getRadsFromDistance(maxDistance),
    num: 10
  };
  console.log('geoOptions: ' + geoOptions);
  if ((!lng && lng!==0) || (!lat && lat!==0) || ! maxDistance) {
    console.log('locationsListByDistance missing params');
    sendJSONresponse(res, 404, {
      "message": "lng, lat and maxDistance query parameters are all required"
    });
    return;
  } else {
    console.log('locationsListByDistance running...');
    Loc.aggregate(
      [{
        '$geoNear': {
          'near': point,
          'spherical': true,
          'distanceField': 'dist.calculated',
          'maxDistance': maxDistance
        }
      }],
      function(err, results) {
        if (err) {
          sendJSONresponse(res, 404, err);
        } else {
          locations = buildLocationList(req, res, results);
          sendJSONresponse(res, 200, locations);
        }
      }
    )
  };
};

var buildLocationList = function(req, res, results) {
  console.log('buildLocationList:');
  var locations = [];
  results.forEach(function(doc) {
      locations.push({
        distance: doc.dist.calculated,
        name: doc.name,
        address: doc.address,
        rating: doc.rating,
        facilities: doc.facilities,
        _id: doc._id
      });
  });
  return locations;
};

returns a result list similar to such:

[
    {
        "distance": 0,
        "name": "Rathaus",
        "address": "Markt",
        "rating": 0,
        "facilities": [
            "museum"
        ],
        "_id": "5a9366517775811a449e503e"
    },
    {
        "distance": 61.77676881925853,
        "name": "Haus Löwenstein",
        "address": "",
        "rating": 0,
        "facilities": [
            "museum"
        ],
        "_id": "5a9366517775811a449e5045"
    },
    {
        "distance": 63.03445976427102,
        "name": "Goldener Schwan",
        "address": "Markt 37",
        "rating": 0,
        "facilities": [
            "restaurant"
        ],
        "_id": "5a9366517775811a449e5052"
    },
    {
        "distance": 66.60375653163021,
        "name": "Klein Printenbäckerei",
        "address": "Krämerstraße 12",
        "rating": 0,
        "facilities": [
            "supermarket"
        ],
        "_id": "5a9366517775811a449e504d"
    },
    {
        "distance": 74.91278395082011,
        "name": "Couven-Museum",
        "address": "Hühnermarkt 17",
        "rating": 0,
        "facilities": [
            "museum"
        ],
        "_id": "5a9366517775811a449e5042"
    },
    {
        "distance": 132.2939512054143,
        "name": "Cathedral Treasury",
        "address": "Johannes-Paul-II.-Straße",
        "rating": 0,
        "facilities": [
            "museum"
        ],
        "_id": "5a9366517775811a449e503d"
    },
    {
        "distance": 152.11867357742042,
        "name": "Aachen Cathedral",
        "address": "Domhof 1",
        "rating": 0,
        "facilities": [
            "museum"
        ],
        "_id": "5a9366517775811a449e503c"
    },
    {
        "distance": 155.92015153163268,
        "name": "International Newspaper Museum",
        "address": "Pontstraße 13",
        "rating": 0,
        "facilities": [
            "museum"
        ],
        "_id": "5a9366517775811a449e5040"
    },
    {
        "distance": 175.0857109968383,
        "name": "Nobis Printen",
        "address": "Münsterplatz 3",
        "rating": 0,
        "facilities": [
            "supermarket"
        ],
        "_id": "5a9366517775811a449e504c"
    },
    {
        "distance": 179.32348875834543,
        "name": "Grashaus",
        "address": "Fischmarkt",
        "rating": 0,
        "facilities": [
            "museum"
        ],
        "_id": "5a9366517775811a449e5044"
    },
    {
        "distance": 189.8675948747873,
        "name": "Maranello",
        "address": "Pontstraße 23",
        "rating": 0,
        "facilities": [
            "restaurant"
        ],
        "_id": "5a9366517775811a449e5057"
    },
    {
        "distance": 198.2239741555585,
        "name": "Carlos I",
        "address": "Rennbahn 1",
        "rating": 0,
        "facilities": [
            "restaurant"
        ],
        "_id": "5a9366517775811a449e5055"
    }
]

Not sure how accurate it is - got a list of addresses loaded and not 100% sure what's close to what in the random mess... but it returns a list and I'll test correctness somehow at some point.

查看更多
我欲成王,谁敢阻挡
3楼-- · 2020-07-08 07:38

I think you're looking for this, please correct me if there is mistake there.

module.exports.locationsListBydistance = function (req, res) {
    var lng = parseFloat(req.query.lng);
    var lat = parseFloat(req.query.lat);
    Loc.aggregate(
        [{
            $geoNear: {
                'near': {'type':'Point', 'coordinates':[lng, lat]},
                'spherical': true,
                'maxdistance': theEarth.getRadsFromDistance(20),
                'num':10,
                'distanceField': 'dist' 
            }
        }
        ], function(err, results) {
            var locations = [];
            console.log(results);
            results.forEach(function (doc) {
                locations.push({
                    distance: theEarth.getDistanceFromRads(doc.dist),
                    name: doc.name,
                    address: doc.address,
                    facilities: doc.facilities,
                    rating: doc.rating,
                    _id: doc._id
                });
            });
            sendJsonResponse(res, 200, locations);
        });
};
查看更多
混吃等死
4楼-- · 2020-07-08 07:38

The answer given by user : phao5814 is quite right I tried it out and must say It worked out well for me

查看更多
对你真心纯属浪费
5楼-- · 2020-07-08 07:41

This error is happening because .geoNear used to be supported, but is no longer supported as of Mongoose 5, which uses the Node MongoDB v3 driver.

The issue is documented in the Migrating to Mongoose 5 document, which in turn links to the MongoDB 3 drive release notes which provides this statement about recommend replacements:

The functionality of the geoNear command is duplicated elsewhere in the language, in the $near/$nearSphere query operators on unsharded collections, and in the $geoNear aggregation stage on all collections.

Effectively, the official docs are endorsing kind of use of $geoNear documented in other answers.

查看更多
萌系小妹纸
6楼-- · 2020-07-08 07:42

I found the solution. Just downgrade mongoose and install version 4.9.1. Latest release of mongoose does not support Loc.geoNear

npm remove mongoose
npm install mongoose@4.9.1
查看更多
狗以群分
7楼-- · 2020-07-08 07:45

More straightforward IMO, than the previous two answers in the Grider's course is:

  index(req, res, next) {
    const { lng, lat } = req.query;
    Driver.find({
      'geometry.coordinates': {
        $nearSphere: {
          $geometry: {
            type: 'Point',
            coordinates:[parseFloat(lng), parseFloat(lat)]
          },
          $maxDistance: 200000,
        },
      }
    })
    .then(drivers => res.send(drivers))
    .catch(next);
  }

This is in the spirit of the original definition he gives and uses the new functions which do the same thing as the old geoNear, except they've split out the spherical and non-spherical versions now. You'll need:

    beforeEach((done) => {
  const { drivers } = mongoose.connection.collections;
  drivers.drop()
    .then(() => drivers.createIndex({ 'geometry.coordinates': '2dsphere' }))
    .then(() => done())
    .catch(() => done());
};

In the test helper as mentioned before.

查看更多
登录 后发表回答