Mongoose/MongoDb 收到错误 geoNear 不是函数

Posted

技术标签:

【中文标题】Mongoose/MongoDb 收到错误 geoNear 不是函数【英文标题】:Mongoose/MongoDb getting error geoNear is not a function 【发布时间】: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

【问题讨论】:

我遇到了完全相同的问题 - 你找到解决方案了吗?? 【参考方案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 非常正确,我试过了,必须说它对我来说效果很好

【讨论】:

欢迎来到 ***。在这里,“我也是”的回答是通过对好的答案投票而不是添加新答案来处理的。【参考方案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(..." 以避免警告。

以上是关于Mongoose/MongoDb 收到错误 geoNear 不是函数的主要内容,如果未能解决你的问题,请参考以下文章

mongoose mongodb nodejs的bodyparser错误

mongoose mongodb nodejs的bodyparser错误

Mongoose + MongoDB - 尝试使用 POSTMAN 保存后,转换为 ObjectID 的值失败

GeoDjango 找不到 geos 库

siddhi gpl执行geo扩展距离函数抛出数据类型错误

Mongoose / Mongodb 迁移到 MySQL