package postgres import ( "context" "fmt" ) type MapExperienceRow struct { PlacePublicID string PlaceName string MapAssetPublicID string MapAssetName string MapCoverURL *string MapSummary *string TileBaseURL *string TileMetaURL *string EventPublicID *string EventDisplayName *string EventSummary *string EventStatus *string EventIsDefaultExperience bool EventShowInEventList bool EventReleasePayloadJSON *string EventPresentationID *string EventPresentationName *string EventPresentationType *string EventPresentationSchema *string EventContentBundleID *string EventContentBundleName *string EventContentEntryURL *string EventContentAssetRootURL *string EventContentMetadataJSON *string } func (s *Store) ListMapExperienceRows(ctx context.Context, limit int) ([]MapExperienceRow, error) { if limit <= 0 || limit > 200 { limit = 50 } rows, err := s.pool.Query(ctx, ` SELECT p.place_public_id, p.name, ma.map_asset_public_id, ma.name, COALESCE(ma.cover_url, p.cover_url) AS cover_url, COALESCE(ma.description, p.description) AS summary, tr.tile_base_url, tr.meta_url, e.event_public_id, e.display_name, e.summary, e.status, COALESCE(e.is_default_experience, false), COALESCE(e.show_in_event_list, true), er.payload_jsonb::text, ep.presentation_public_id, ep.name, ep.presentation_type, ep.schema_jsonb::text, cb.content_bundle_public_id, cb.name, cb.entry_url, cb.asset_root_url, cb.metadata_jsonb::text FROM map_assets ma JOIN places p ON p.id = ma.place_id LEFT JOIN tile_releases tr ON tr.id = ma.current_tile_release_id LEFT JOIN map_runtime_bindings mrb ON mrb.map_asset_id = ma.id AND mrb.status = 'active' LEFT JOIN event_releases er ON er.runtime_binding_id = mrb.id AND er.status = 'published' LEFT JOIN events e ON e.current_release_id = er.id AND e.status = 'active' LEFT JOIN event_presentations ep ON ep.id = er.presentation_id LEFT JOIN content_bundles cb ON cb.id = er.content_bundle_id WHERE ma.status = 'active' AND (e.id IS NULL OR e.show_in_event_list = true) ORDER BY p.name ASC, ma.name ASC, COALESCE(e.is_default_experience, false) DESC, e.display_name ASC LIMIT $1 `, limit) if err != nil { return nil, fmt.Errorf("list map experience rows: %w", err) } defer rows.Close() items := make([]MapExperienceRow, 0, limit) for rows.Next() { var item MapExperienceRow if err := rows.Scan( &item.PlacePublicID, &item.PlaceName, &item.MapAssetPublicID, &item.MapAssetName, &item.MapCoverURL, &item.MapSummary, &item.TileBaseURL, &item.TileMetaURL, &item.EventPublicID, &item.EventDisplayName, &item.EventSummary, &item.EventStatus, &item.EventIsDefaultExperience, &item.EventShowInEventList, &item.EventReleasePayloadJSON, &item.EventPresentationID, &item.EventPresentationName, &item.EventPresentationType, &item.EventPresentationSchema, &item.EventContentBundleID, &item.EventContentBundleName, &item.EventContentEntryURL, &item.EventContentAssetRootURL, &item.EventContentMetadataJSON, ); err != nil { return nil, fmt.Errorf("scan map experience row: %w", err) } items = append(items, item) } if err := rows.Err(); err != nil { return nil, fmt.Errorf("iterate map experience rows: %w", err) } return items, nil } func (s *Store) ListMapExperienceRowsByMapPublicID(ctx context.Context, mapAssetPublicID string) ([]MapExperienceRow, error) { rows, err := s.pool.Query(ctx, ` SELECT p.place_public_id, p.name, ma.map_asset_public_id, ma.name, COALESCE(ma.cover_url, p.cover_url) AS cover_url, COALESCE(ma.description, p.description) AS summary, tr.tile_base_url, tr.meta_url, e.event_public_id, e.display_name, e.summary, e.status, COALESCE(e.is_default_experience, false), COALESCE(e.show_in_event_list, true), er.payload_jsonb::text, ep.presentation_public_id, ep.name, ep.presentation_type, ep.schema_jsonb::text, cb.content_bundle_public_id, cb.name, cb.entry_url, cb.asset_root_url, cb.metadata_jsonb::text FROM map_assets ma JOIN places p ON p.id = ma.place_id LEFT JOIN tile_releases tr ON tr.id = ma.current_tile_release_id LEFT JOIN map_runtime_bindings mrb ON mrb.map_asset_id = ma.id AND mrb.status = 'active' LEFT JOIN event_releases er ON er.runtime_binding_id = mrb.id AND er.status = 'published' LEFT JOIN events e ON e.current_release_id = er.id AND e.status = 'active' LEFT JOIN event_presentations ep ON ep.id = er.presentation_id LEFT JOIN content_bundles cb ON cb.id = er.content_bundle_id WHERE ma.map_asset_public_id = $1 AND ma.status = 'active' AND (e.id IS NULL OR e.show_in_event_list = true) ORDER BY COALESCE(e.is_default_experience, false) DESC, e.display_name ASC `, mapAssetPublicID) if err != nil { return nil, fmt.Errorf("list map experience rows by map public id: %w", err) } defer rows.Close() items := make([]MapExperienceRow, 0, 8) for rows.Next() { var item MapExperienceRow if err := rows.Scan( &item.PlacePublicID, &item.PlaceName, &item.MapAssetPublicID, &item.MapAssetName, &item.MapCoverURL, &item.MapSummary, &item.TileBaseURL, &item.TileMetaURL, &item.EventPublicID, &item.EventDisplayName, &item.EventSummary, &item.EventStatus, &item.EventIsDefaultExperience, &item.EventShowInEventList, &item.EventReleasePayloadJSON, &item.EventPresentationID, &item.EventPresentationName, &item.EventPresentationType, &item.EventPresentationSchema, &item.EventContentBundleID, &item.EventContentBundleName, &item.EventContentEntryURL, &item.EventContentAssetRootURL, &item.EventContentMetadataJSON, ); err != nil { return nil, fmt.Errorf("scan map experience detail row: %w", err) } items = append(items, item) } if err := rows.Err(); err != nil { return nil, fmt.Errorf("iterate map experience detail rows: %w", err) } return items, nil }