Mongoose/MongoDb出现错误,提示geoNear不是一个函数。

8

这是我的控制器文件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不是一个函数”。
以下是我使用的依赖版本:
- node: 8.9.3 - npm: 5.5.1 - express: 4.15.5 - mongoose: 5.0.0 - mongoDB: 3.6.1
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) 在next (/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) 在next (/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) 在router (/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) 在next (/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

2
我遇到了完全相同的问题 - 你找到解决方案了吗? - ptk
12个回答

9

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

8
这个错误是因为在Mongoose 5中不再支持.geoNear,而是使用Node MongoDB v3驱动程序。该问题在迁移到Mongoose 5文档中有记录,并链接到MongoDB 3驱动程序发布说明,其中提供了关于推荐替代方案的声明:在未分片集合上的$near/$nearSphere查询操作和所有集合上的$geoNear聚合阶段中都有重复的geoNear命令功能。实际上,官方文档支持在其他答案中记录的$geoNear的使用方式。

3

显然我和你在同一本书中(《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"
    }
]

不确定它的准确性 - 已加载地址列表,不确定在随机混乱中哪个地址靠近哪个...但它会返回一个列表,我会在某个时候测试其正确性。


1
你的代码中不再需要geoOptions对象,因为你已经在聚合管道中使用了$geoNear属性,而无需传递刚声明在控制台日志下方的对象。 - rags2riches-prog
1
还有一件事,您还需要更改数据模型和设置: 数据模型 => coords: {type: {type: String, default: "Point"}, coordinates: {type: [Number]}} 数据集 => "coords" : { "type": "Point", "coordinates": [-0.9690884, 51.455041] } - bugrasan

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

我这里也有同样的问题,但是我不理解聚合函数的用法,能否解释一下? - krekto

2

我认为比 Grider 课程中前两个答案更加简单明了的是:

  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 聚合,但是当我看到这个解决方案后,我完全用这个最简单的解决方案替换了聚合。 - TalESid

2

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

npm remove mongoose
npm install mongoose@4.9.1

0

我认为你正在寻找这个,请纠正我如果有错误。

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

0

Model.geoNear()已被删除,因为MongoDB驱动程序不再支持它,所以您应该使用聚合方法,例如: 对于mongoose v4.2: 我的代码是这样的:

Modal.geoNear(
    {type: 'Point', coordinates: [parseFloat(req.query.lng), parseFloat(req.query.lat)]},
    {maxDistance: 100000, spherical: true}
)
.then(results => {
    res.send(results);
})

对于最新的mongoose版本: 代码如下:

 Modal.aggregate([{
        $geoNear: {
            near: {
                type: 'Point',
                coordinates: [parseFloat(req.query.lng), parseFloat(req.query.lat)]
            },
            distanceField: 'distance',
            maxDistance: 100000,
            spherical: true
        }
    }])
    .then(results => {
        res.send(results);
    })

希望我解答或解决了您的问题,愉快地编程吧 :D

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

1
你能解释一下你的代码以及它是如何回答这个问题的吗? - tshimkus

0

Model.geoNear()已被删除,因为MongoDB驱动程序不再支持它


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接