【JS/TS】geofirestoreを使って位置情報を検索条件で使用する


はじめに

現在ReactNativeで開発中のアプリのバックエンドとしてFirebaseを使用しています。
今さら説明の必要はないかと思いますがFirebaseは、プッシュ通知や認証などを一手に引き受けてくれる優れもので、firestoreというNoSQLDBもあります。
無料枠でも使いこなせば高クオリティのものが出来上がるため重宝しているのですが、今回firestoreに格納したデータの「位置情報」に対して検索条件を付けたくなり、少し手間だったので記事にします。
※イメージとして、 「現在地から半径Nキロ以内」のデータを取得してきてマップにプロット するようなアプリを想定しています。

GeoPoint

firestoreにはGeoPointという形式で位置情報が保存されます。

値の保持はこれで問題ないのですが、この値を基にクエリを作成しようとした時に問題が発生します。
「緯度と経度を用いた複合条件ができない」 ためです。
そのあたりで困っている方はやはり多いようで、有志の方が作成した拡張ライブラリがありました。
geofirestoreというものです。

インストール

yarn geofirestore

使い方

基本形

firebaseのチュートリアルに沿って実装している場合は組み込みは容易です。
まず、通常のfirestoreインスタンスから新たにGeoFirestoreインスタンスを作成します。

import firebase from 'firebase/app';
import * as Geofirestore from 'geofirestore';

// まずはfirebaseインスタンスを初期化
const firebaseApp: firebase.app.App = firebase.initializeApp({
  apiKey: "xxxxxxx",
  /* ...略... */
});

// firestoreインスタンスを作成
const db: firebase.firestore.Firestore = firebaseApp.firestore();
// firestoreインスタンスからGeoFirestoreインスタンスを作成
const gdb: Geofirestore.GeoFirestore = Geofirestore.initializeApp(db);

データ作成時

GeoFirestoreインスタンスはfirestoreインスタンスとほぼ同じ使い勝手で利用することができます。
位置情報を持たせてクエリを投げるためcoordinatesというフィールド名でGeoPoint型の値を格納します。

gdb.collection('test').add({
    data1: 'hogehoge',
    data2: 'fugafuga',
    // coordinatesというフィールド名でGeoPointを設定(引数にはlat,lon)
    coordinates: new firebase.firestore.GeoPoint(35.306767, 138.9606928),
})

成功するとcoordinates以外にもgというフィールドが作成されていました。
geohash等の細かい値を持っているので、後述のクエリはこちらの値を参照しているのかもしれません。

g_field

クエリ実行時

GeoPointに関わらない値を扱う場合のクエリ(where()等)はfirestoreと使い勝手が同じです。
GeoFirestoreにおいてGeoPointに関するクエリを扱う場合はnear()を使います。

gdb.collection('test').near({ 
    // 中心となる座標をGeoPointで指定
    center: new firebase.firestore.GeoPoint(35.306767, 138.9606928), 
    // 中心座標からの半径(km)を指定
    radius: 5 
}).get().then((d) => {
    // ...略
});

まとめ

今回はgeofirestoreを利用したFirestore中のGeoPointを使った検索クエリの発行方法について紹介しました。
素のFirestoreだけでなくreact-native-firestoreでも同様に動作したので、Webやモバイルを問わずに利用できます。

使い勝手もfirestoreの書き方をそのまま流用できるので、位置情報絡みのアプリケーションを開発する場合はオススメです。