在mongoose中创建和查找地理位置

23

我想在用户模型中保存位置的经度/纬度,并使用$geoNear查询它。我查看了几乎所有相关主题的页面,但仍然不确定如何 1)适当地创建模型和2)如何进行查询。

以下是我的模型部分:

loc: {
        //type: 'Point', // see 1
        //type: String, // see 2
        coordinates: [Number],
        index: '2dsphere' // see 3
    },

@1: 根据文档,如果我想存储一个点,类型必须为“Point”,但它会抛出异常。

TypeError: Undefined type `Point` at `loc`
  Did you try nesting Schemas? You can only nest using refs or arrays.

这是有道理的,因为我猜它应该是一个字符串

@2:这不会立即报错,但当我尝试存储用户时,会出现以下错误:

name: 'ValidationError',
  errors: 
   { loc: 
      { [CastError: Cast to String failed for value "[object Object]" at path "loc"]

@3:如果我想创建这样的索引,我会收到错误提示:

TypeError: Undefined type `2dsphere` at `loc.index`
  Did you try nesting Schemas? You can only nest using refs or arrays.

因此,我尝试将索引存储到我认为更合适的位置,如下所示:

userSchema.index({'loc': '2dsphere'});

当我尝试保存新用户时,出现以下错误:

- { [MongoError: Can't extract geo keys from object, malformed geometry?:{ coordinates: [ -73.97, 40.77 ] }]
  name: 'MongoError',
  message: 'Can\'t extract geo keys from object, malformed geometry?:{ coordinates: [ -73.97, 40.77 ] }',
  ok: 1,
  n: 0,
  code: 16572,
  errmsg: 'Can\'t extract geo keys from object, malformed geometry?:{ coordinates: [ -73.97, 40.77 ] }',
  writeConcernError: 
   { code: 16572,
     errmsg: 'Can\'t extract geo keys from object, malformed geometry?:{ coordinates: [ -73.97, 40.77 ] }' } }
MongoError: Can't extract geo keys from object, malformed geometry?:{ coordinates: [ -73.97, 40.77 ] }

当我将数据库中的模型更新为$geoNear命令时,我像这样更新了我的用户:
loc: {coordinates: [-73.97, 40.77]}

并且可以通过以下方式成功查询:

 mongoose.model('users').db.db.command({
    "geoNear": users,
    "near": [
        <latitude>,
        <longitude>
    ],
    "spherical": true,
    "distanceMultiplier": 6378.1, // multiplier for kilometres
    "maxDistance": 100 / 6378.1, // every user within 100 km
    "query": { }

我使用了 db.command,因为根据我所读的资料,在使用 find()$geoNear 是不可用的。

到目前为止,我尝试过的所有方法都没有成功,所以我的问题是:

  • 我的 mongoose 模型应该是什么样子的?
  • 如何将用户及其位置存储在我的数据库中?

任何帮助都将不胜感激!

3个回答

72
如果您想要一个支持GeoJSON的模式,那么首先需要正确地构建它:
var userSchema = new Schema({
    loc: {
        type: { type: String },
        coordinates: [Number],
    }
});

这样可以确保模式定义中的“type”关键字不会混淆。如果你真的想支持整个 GeoJSON 类型范围,那么你可以让它更宽松一些:

var userSchema = new Schema({
    loc: {
        type: { type: String },
        coordinates: []
    }
});

接下来,您需要将索引绑定到模式:

userSchema.index({ "loc": "2dsphere" });

然后当然要定义一个模型并正确地存储东西:

var User = mongoose.model( "User", userSchema );

 var user = new User({ 
     "loc": { 
         "type": "Point",
         "coordinates": [-73.97, 40.77]
     }
 });

请注意,您的数据必须按照地理JSON和所有MongoDB地理空间查询形式所支持的经度然后是纬度的顺序。

接下来,不要直接在原始驱动程序方法上挖掘数据库命令的晦涩用法,而应使用直接支持且更好的内容。例如,对于.aggregate()方法,请使用$geoNear

User.aggregate(
    [
        { "$geoNear": {
            "near": {
                "type": "Point",
                "coordinates": [<long>,<lat>]
            },
            "distanceField": "distance",
            "spherical": true,
            "maxDistance": 10000
        }}
    ],
    function(err,results) {

    }
)

现在由于数据是GeoJSON格式,距离已经转换为米,因此无需进行其他转换工作。

还要注意的是,如果您一直在尝试修改它,除非您删除集合中的任何索引,否则您尝试过的所有索引仍将存在,这可能会导致问题。

您可以轻松在mongodb shell中删除集合中的所有索引:

db.users.dropIndexes();

或者,如果您需要对数据进行一些重塑,那么请删除集合并重新开始:

db.users.drop();

妥善安排事物,就不会出现问题。


谢谢你的帮助!现在用户的存储正常工作。我也会看一下你的“聚合”实现,看起来比我之前使用的那种hack要好得多。 - Markus
2
谢谢您提供这个信息,请在您的地理查询中使用“spherical”而不是“sperical”。 - Colo Ghidini
@ColoGhidini 你能解释一下这句话吗:“接下来,与其直接在原始驱动程序方法上深入研究数据库命令的晦涩用法,不如使用那些得到直接支持且更好的东西。” - Jessica
1
这种方法已经不再适用了。即使将索引添加到模式中,它仍会返回以下错误:$geoNear需要2d或2dsphere索引。 - DoneDeal0

0

这对我来说是点和多边形更好的解决方案。

var WeSchema = new Schema({
Location: {
    type: {
      type: String,
      enum: ['Point', 'Polygon']
    },
    coordinates: [Number] 
  }
});
WeSchema.index({Location: '2dsphere' });

0
我遇到了以下错误:
ApolloError: geoNear command failed: { ok: 0.0, errmsg: "no geo indices for geoNear" }

代码:

const mongoose = require("mongoose");

mongoose.Promise = global.Promise;

const ToDoSchema = new mongoose.Schema(
  {
    title: { type: String, required: true },
    gmap: { geoJson: { type: { type: String }, coordinates: [Number] } },
  },
  { timestamps: true }
);
ToDoSchema.index({ "gmap.geoJson": "2dsphere" });

export default mongoose.models.things_to_do ||
  mongoose.model("things_to_do", ToDoSchema, "things_to_do");

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