使用 GeoJSON

GeoJSON 是一种用于存储地理点和多边形的格式。 MongoDB 对 GeoJSON 对象的地理空间查询提供出色支持。 让我们看看如何使用 Mongoose 存储和查询 GeoJSON 对象。

点模式

GeoJSON 中最简单的结构是一个点。 以下是一个示例点,代表 旧金山 的大致位置。 请注意,在 GeoJSON 坐标数组中,经度优先于纬度,**而不是** 纬度。

{
  "type" : "Point",
  "coordinates" : [
    -122.5,
    37.7
  ]
}

下面是一个 Mongoose 模式示例,其中 location 是一个点。

const citySchema = new mongoose.Schema({
  name: String,
  location: {
    type: {
      type: String, // Don't do `{ location: { type: String } }`
      enum: ['Point'], // 'location.type' must be 'Point'
      required: true
    },
    coordinates: {
      type: [Number],
      required: true
    }
  }
});

使用 子文档,您可以定义一个通用的 pointSchema,并在您想要存储 GeoJSON 点的任何地方重复使用它。

const pointSchema = new mongoose.Schema({
  type: {
    type: String,
    enum: ['Point'],
    required: true
  },
  coordinates: {
    type: [Number],
    required: true
  }
});

const citySchema = new mongoose.Schema({
  name: String,
  location: {
    type: pointSchema,
    required: true
  }
});

多边形模式

GeoJSON 多边形允许您在地图上定义任意形状。 例如,下面的多边形是一个 GeoJSON 矩形,它近似于科罗拉多州的边界。

{
  "type": "Polygon",
  "coordinates": [[
    [-109, 41],
    [-102, 41],
    [-102, 37],
    [-109, 37],
    [-109, 41]
  ]]
}

多边形很棘手,因为它们使用三级嵌套数组。 下面是如何创建一个 Mongoose 模式,其中 coordinates 是一个三级嵌套的数字数组。

const polygonSchema = new mongoose.Schema({
  type: {
    type: String,
    enum: ['Polygon'],
    required: true
  },
  coordinates: {
    type: [[[Number]]], // Array of arrays of arrays of numbers
    required: true
  }
});

const citySchema = new mongoose.Schema({
  name: String,
  location: polygonSchema
});

使用 Mongoose 进行地理空间查询

Mongoose 查询支持与 MongoDB 驱动程序相同的 地理空间查询运算符。 例如,下面的脚本保存一个 city 文档,其 location 属性是一个 GeoJSON 点,代表科罗拉多州丹佛市。 然后,它使用 MongoDB 的 $geoWithin 运算符 查询位于表示科罗拉多州的多边形内的所有文档。

Colorado GeoJSON Polygon
const City = db.model('City', new Schema({
  name: String,
  location: pointSchema
}));

const colorado = {
  type: 'Polygon',
  coordinates: [[
    [-109, 41],
    [-102, 41],
    [-102, 37],
    [-109, 37],
    [-109, 41]
  ]]
};
const denver = { type: 'Point', coordinates: [-104.9903, 39.7392] };
return City.create({ name: 'Denver', location: denver }).
  then(() => City.findOne({
    location: {
      $geoWithin: {
        $geometry: colorado
      }
    }
  })).
  then(doc => assert.equal(doc.name, 'Denver'));

Mongoose 还具有 within() 帮助器,它作为 $geoWithin 的简写。

const denver = { type: 'Point', coordinates: [-104.9903, 39.7392] };
return City.create({ name: 'Denver', location: denver }).
  then(() => City.findOne().where('location').within(colorado)).
  then(doc => assert.equal(doc.name, 'Denver'));

地理空间索引

MongoDB 支持 2dsphere 索引 以加速地理空间查询。 以下是您如何在 GeoJSON 点上定义 2dsphere 索引的方法

const denver = { type: 'Point', coordinates: [-104.9903, 39.7392] };
const City = db.model('City', new Schema({
  name: String,
  location: {
    type: pointSchema,
    index: '2dsphere' // Create a special 2dsphere index on `City.location`
  }
}));

return City.create({ name: 'Denver', location: denver }).
  then(() => City.findOne().where('location').within(colorado)).
  then(doc => assert.equal(doc.name, 'Denver'));

您也可以使用 Schema#index() 函数 来定义地理空间索引,如下所示。

citySchema.index({ location: '2dsphere' });

MongoDB 的 $near 查询运算符$geoNear 聚合阶段 需要 2dsphere 索引。