Postgres で Function 定義
landmark テーブルで、指定した中心点と半径に含まれるレコードを、近い順に並べ抽出する。
-
landmarks
テーブルを作成 -
array_to_geography()
Function を作成する。- array ([longitude, latitude])を
geography
に変換する。
- array ([longitude, latitude])を
-
geo_landmarks()
Function では geography の値を使用し、パラメーターの円(中心点と半径)に含まれる landmark を円の中心点より近い順に並べて返す。
create table landmarks (
id uuid default gen_random_uuid(),
name text not null,
description text,
point double precision[],
geopoint geography(point, 4326),
location jsonb not null default '{}',
primary key (id)
);
create or replace function array_to_geography() returns trigger as $$
begin
new.geopoint = st_setsrid(st_point(new.point[1], new.point[2]), 4326)::geography;
new.location = json_build_object('longitude', new.point[1], 'latitude', new.point[2]);
return new;
end;
$$ language plpgsql;
create trigger trigger_array_to_geography
before insert or update on landmarks
for each row execute procedure array_to_geography();
create or replace function public.geo_landmarks(
latitude double precision,
longitude double precision,
radius integer
) returns setof public.landmarks as $$
select
id,
name,
description,
point,
geopoint,
case
when point is null then '{}'::jsonb
else json_build_object(
'longitude',
point[1],
'latitude',
point[2],
'distance',
st_distance(st_point(longitude, latitude)::geography, geopoint)
)::jsonb
end as location
from
landmarks
where
st_dwithin(geopoint, st_point(longitude, latitude)::geography, radius)
order by
st_distance(st_point(longitude, latitude)::geography, geopoint),
name asc
;
$$ language sql stable;
よくよく見ると、それ必要?といった所もあるが大目に見る。
上記定義と Hasura で API を試す
Hasura を起動する
git clone https://github.com/high-u/tryout-hasura.git
cd tryout-hasura/postgis
docker compose up -d
しばし待つ...しばし待つ...
ブラウザで Hasura を開く
GraphQL に
query landmarksIncludedInCircle(
$latitude: float8 = "",
$longitude: float8 = "",
$radius: Int = 100
) {
landmarks: geo_landmarks(args: {
latitude: $latitude,
longitude: $longitude,
radius: $radius
}) {
id
name
location
}
}
QUERY VARIABLES に
{
"latitude": 35.65867828258049,
"longitude": 139.7454000428654,
"radius": 500
}
を押すと API が実行されレスポンスが表示される。
そもそもどんなデータが登録されているかは、ここで確認。
後始末
docker compose down -v
あとがき
大量のアクセスをさばく必要がある場合には要注意だが、PostGIS を利用した距離計算付きの API が GraphQL としてあっという間に構築できることに感動した。
謝辞
下記記事に多くを学ばせていただきました。