【问题标题】:Mongoose/MongoDb getting error geoNear is not a functionMongoose/MongoDb 收到错误 geoNear 不是函数
【发布时间】:2018-06-14 08:25:48
【问题描述】:

这是我的控制器文件 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);
  });
};

我的模型文件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');

每当我运行 http://localhost:3000/api/locations?lng=-0.9690884&lat=51.455041 时,我都会收到错误 geoNear is not a function

TypeError: Loc.geoNear 不是函数 在 module.exports.locationsListByDistance (/home/shackers/Projects/mean/loc8r/app_api/controllers/locations.js:51:7) 在 Layer.handle [as handle_request] (/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/layer.js:95:5) 在下一个(/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/route.js:137:13) 在 Route.dispatch (/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/route.js:112:3) 在 Layer.handle [as handle_request] (/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/layer.js:95:5) 在/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/index.js:281:22 在 Function.process_params (/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/index.js:335:12) 在下一个(/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/index.js:275:10) 在 Function.handle (/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/index.js:174:3) 在路由器(/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/index.js:47:12) 在 Layer.handle [as handle_request] (/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/layer.js:95:5) 在 trim_prefix (/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/index.js:317:13) 在/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/index.js:284:7 在 Function.process_params (/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/index.js:335:12) 在下一个(/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/index.js:275:10) 在 /home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/index.js:635:15

这是我正在使用的依赖版本:

  • 节点:8.9.3 npm:5.5.1 express:4.15.5 mongoose:5.0.0 mongoDb:3.6.1

【问题讨论】:

  • 我遇到了完全相同的问题 - 你找到解决方案了吗??

标签: node.js mongodb mongoose


【解决方案1】:
router.get('/', () => {
    Loc.aggregate([
        {
            $geoNear: {
                near: 'Point',
                distanceField: "dist.calculated",
                maxDistance: 100000,
                spherical: true                
            }
        }
    ]).then(function(err, results, next){
        res.send();
    }).catch(next);
});

参考:-https://docs.mongodb.com/manual/reference/command/geoNear/

【讨论】:

  • 我这里有同样的问题,但是我不明白聚合的使用,可以解释一下吗?
  • @krekto 我不是 Mongo 专家,但这里是 Mongo 聚合管道的相关 documentation。我相信聚合管道只是在数据库中“查找”文档的另一种方式,并且恰好也具有 geoNear 功能。如果我错了,欢迎其他人纠正我。
  • @krekto-db.students.aggregate([ { $grades: { rank: 'A', group: { _id: "$student_id", total: { $sum: "$score" } }, sort: { total: -1 } } } ]) 代码美化/缩进这个例子。上面的操作选择排名等于“A”的学生文档,按照 student_id 字段对匹配的文档进行分组,并计算每个文档的总和student_id 字段从金额字段的总和,并按总字段的降序对结果进行排序。我希望这有助于理解聚合。
  • @deechris27 明白了,tx
【解决方案2】:

发生此错误是因为 .geoNear 曾经受支持,但从使用 Node MongoDB v3 驱动程序的 Mongoose 5 开始不再受支持。

Migrating to Mongoose 5 文档中记录了该问题,该文档又链接到 MongoDB 3 drive release notes,该文档提供了有关推荐替换的声明:

geoNear 命令的功能在语言的其他地方、未分片集合的 $near/$nearSphere 查询运算符以及所有集合的 $geoNear 聚合阶段都有重复。

实际上,官方文档认可在其他答案中记录的$geoNear 的使用。

【讨论】:

    【解决方案3】:

    我遇到了完全相同的问题,我已经放弃了以前使用的方法(看起来你也有)。以下是一种不会引发错误的替代方法,并且应该会为您提供与使用 Loc.geoNear 后相同的结果:

    Loc.aggregate(
            [
                {
                    '$geoNear': {
                        'near': point,
                        'spherical': true,
                        'distanceField': 'dist',
                        'maxDistance': 1000
                    }
                }
            ],
            function(err, results) {
                // do what you want with the results here
            }
        )
    

    【讨论】:

    • 我这里有同样的问题,但是我不明白聚合的使用,可以解释一下吗?
    【解决方案4】:

    显然,我在同一本书(Getting Mean, Manning)中遇到了大致相同的问题。这似乎对我有用:

    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;
    };
    

    返回一个类似这样的结果列表:

    [
        {
            "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"
        }
    ]
    

    不确定它有多准确 - 加载了一个地址列表,并且不能 100% 确定随机混乱中的内容是什么……但它会返回一个列表,我会在某个时候以某种方式测试正确性。

    【讨论】:

    • 您的代码中不再需要对象 geoOptions,因为您已经在聚合中使用了 $geoNear 属性,而没有传递在控制台日志正下方声明的对象。
    • 另外一件事,你还需要改变数据模型&集合:数据模型=>coords: {type: {type: String, default: "Point"}, coordinates: {type: [Number]}}数据集=>"coords" : { "type": "Point", "coordinates": [-0.9690884, 51.455041] }
    【解决方案5】:

    我找到了解决方案。只需降级猫鼬并安装版本 4.9.1。最新版本的 mongoose 不支持 Loc.geoNear

    npm remove mongoose
    npm install mongoose@4.9.1
    

    【讨论】:

      【解决方案6】:

      比 Grider 课程中的前两个答案更直接的 IMO 是:

        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);
        }
      

      这与他给出的原始定义的精神相一致,并使用了与旧 geoNear 做同样事情的新函数,除了它们现在已经分离出球形和非球形版本。你需要:

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

      在前面提到的测试助手中。

      【讨论】:

      • 最好和最简单的出路。我正在使用$geoNear aggregate 但是当我完成这个解决方案时,我用这个最简单的解决方案完全替换了聚合
      【解决方案7】:

      我想你正在寻找这个,如果那里有错误,请纠正我。

      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);
              });
      };
      

      【讨论】:

        【解决方案8】:
        router.get('/',function(req,res,next){
            Loc.aggregate([
                {
                    $geoNear: {
                        near: {type:'Point', coordinates:[parseFloat(req.query.lng), parseFloat(req.query.lat)]},
                        distanceField: "dist.calculated",
                        maxDistance: 1000,
                        spherical: true                
                    }
                }
            ]).then(function(Locs){
                res.send(Locs)
            })
        })
        

        【讨论】:

        • 你能解释一下你的代码以及它是如何回答这个问题的吗?
        【解决方案9】:

        用户给出的答案:phao5814 非常正确,我试过了,必须说它对我来说效果很好

        【讨论】:

        • 欢迎来到 StackOverflow。在这里,“我也是”的回答是通过对好的答案投票而不是添加新答案来处理的。
        【解决方案10】:
         index(req, res, next) 
        {
           const { lng, lat } = req.query;
           Driver.aggregate([
           {
             '$geoNear': {
                            "near": { 'type': 'Point', 
                            'coordinates': [parseFloat(lng), parseFloat(lat)] },
                            "spherical": true, 
                            "distanceField": 'dist', 
                            "maxDistance": 200000
                        }
                    }
                ])
                    .then(drivers => res.send(drivers))
                    .catch(next);
            }
        

        【讨论】:

        • 如果您正在学习当前托管在 Udemy 上的 Stephen Grider 的课程“MongoDB 的完整开发人员指南”,上面的代码可以完美运行。确保将 beforeEach 属性更改为 ".then(() => drivers.createIndexes(..." 以避免警告。
        猜你喜欢
        • 2018-08-11
        • 1970-01-01
        • 2021-07-18
        • 1970-01-01
        • 2016-10-08
        • 2019-08-05
        • 2021-08-28
        • 2019-02-20
        • 2022-11-07
        相关资源
        最近更新 更多