データモデルData model

Web 版は Supabase を読み取り専用で使います。スキーマの全容は 共通データモデル を参照してください。 ここでは Web 版が ビルド時 / ランタイムにどのテーブルをどう使うかだけを示します。

The web app reads from Supabase only. For the full schema see the shared data model. This page only lists which tables the web app touches at build time vs. runtime.

ビルド時に参照するテーブルTables read at build time

Table用途Purpose
parking_lotsスポット詳細 / ハブページ生成Spot detail and hub pages
parking_lot_imagesギャラリーGalleries
parking_lot_hours営業時間表示Hours display
parking_lot_pricing_rules料金表Pricing tables
parking_lot_attributes設備・特徴Facilities & features
parking_lot_tags + tagsタグ(シーン別マッチ)Tags — for scene matching
parking_reviews平均星・レビュー抜粋Aggregated rating and excerpts
articlesメディア記事の一部(見出し・メタ)Media article metadata
codes列挙値のラベル解決Enum label resolution

ランタイムで参照するテーブルTables read at runtime

原則、Web 版はランタイムで Supabase から直接テーブルを読みません。 /search の全文検索は Pagefind が生成した静的インデックス (dist/pagefind/) をクライアントから fetch して完結します。 検索対象の本体データは、Pagefind がビルド時に各ページの HTML を走査してインデックスに埋め込みます。

By default the web app does not read Supabase at runtime. Full-text search on /search runs entirely against the Pagefind static index (dist/pagefind/) fetched by the client. Pagefind scans the built HTML at post-build time and embeds everything it needs.

例外的にクライアントから Supabase に anon キーで問い合わせる島が追加される場合は、 以下の RLS 前提が必須になります。

Should an island be added that really needs an anon-key query against Supabase at runtime, it must assume the RLS policies below.

RLS ポリシー: RLS policies: ランタイムで anon が読むテーブルには、deleted_at IS NULL AND published = true 等の公開条件でフィルタする RLS を必ず設定する。 Any table read by anon at runtime must have an RLS policy filtering to something like deleted_at IS NULL AND published = true.

代表的なクエリパターンTypical queries

スポット詳細の集約 (build)Spot detail aggregate (build)

select pl.*,
       (select json_agg(i) from parking_lot_images i where i.parking_lot_id = pl.id) as images,
       (select json_agg(h) from parking_lot_hours h where h.parking_lot_id = pl.id) as hours,
       (select json_agg(pr) from parking_lot_pricing_rules pr where pr.parking_lot_id = pl.id) as pricing,
       (select avg(rating)::numeric(3,2) from parking_reviews where parking_lot_id = pl.id) as avg_rating
from parking_lots pl
where pl.id = $1 and pl.deleted_at is null;

近隣スポット(PostGIS)Nearby spots (PostGIS)

select id, name, st_distance(location, st_makepoint($1, $2)::geography) as dist_m
from parking_lots
where deleted_at is null
order by location <-> st_makepoint($1, $2)::geography
limit 20;