GeoJSON を使用

GeoJSON は、地理的なポイントとポリゴンを格納するための形式です。 MongoDB は、GeoJSON オブジェクトの地理空間クエリを強力にサポートしています。Mongoose を使用して GeoJSON オブジェクトを格納してクエリする方法を見ていきましょう。

ポイントスキーマ

GeoJSON の最もシンプルな構造はポイントです。以下は、サンフランシスコ のおおよその場所を表すポイントの例です。GeoJSON 座標の配列では、経度が緯度ではなく最初に来ますのでご注意ください。

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

以下は、location がポイントである Mongoose スキーマの例です。

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

ポリゴンは 3 重にネストされた配列を使用するため、扱いが厄介です。以下は、coordinates が数値の 3 重にネストされた配列である Mongoose スキーマを作成する方法を示します。

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 ドライバーと同じ 地理空間クエリ演算子 をサポートしています。たとえば、以下のスクリプトでは、location プロパティがコロラド州デンバー市を表す GeoJSON ポイントで city ドキュメントを保存します。その後、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 には、$geoWithin の省略記号である within() ヘルパー もあります。

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 インデックスが必須です。