Skip to content

Latest commit

ย 

History

History
836 lines (677 loc) ยท 47.8 KB

File metadata and controls

836 lines (677 loc) ยท 47.8 KB

StationAPI ใ‚ขใƒผใ‚ญใƒ†ใ‚ฏใƒใƒฃใƒ‰ใ‚ญใƒฅใƒกใƒณใƒˆ

ๆœ€็ต‚ๆ›ดๆ–ฐ: 2026ๅนด9ๆœˆ29ๆ—ฅ

็›ฎๆฌก


ๆฆ‚่ฆ

ๆ—ฅๆœฌใฎ้‰„้“้ง…ใจใƒใ‚นๅœใฎๆƒ…ๅ ฑใ‚’่ฟ”ใ™ GraphQL API ใงใ™ใ€‚Cloudflare Workers ไธŠใง ๅ‹•ไฝœใ—ใ€ใƒ‡ใƒผใ‚ฟใฏใƒ“ใƒซใƒ‰ๆ™‚ใซ WASM ใƒใ‚คใƒŠใƒชใธๅŸ‹ใ‚่พผใ‚“ใง้…ๅธƒใ—ใพใ™ใ€‚ ใ‚ตใƒผใƒใƒผใƒ—ใƒญใ‚ปใ‚นใ‚‚ใƒ‡ใƒผใ‚ฟใƒ™ใƒผใ‚นใ‚‚ใ‚ใ‚Šใพใ›ใ‚“ใ€‚

ไปฅๅ‰ใฏใ€ใ‚ชใƒณใƒ—ใƒฌใƒŸใ‚นใฎใ‚ตใƒผใƒใƒผใŒ PostgreSQL ใ‹ใ‚‰ใƒ‡ใƒผใ‚ฟใ‚’่ชญใ‚“ใง gRPC-Web ใง ่ฟ”ใ—ใ€ใ‚ฏใƒฉใ‚คใ‚ขใƒณใƒˆใฏ BFF (TrainLCD/BFF) ใŒ GraphQL ใซๅค‰ๆ›ใ—ใŸใ‚‚ใฎใ‚’ไฝฟใฃใฆ ใ„ใพใ—ใŸใ€‚gRPC-Web ใ‚’ไฝฟใ„็ถšใ‘ใ‚‹็†็”ฑใŒใชใ‹ใฃใŸใŸใ‚ใ€GraphQL ใ‚’็›ดๆŽฅ่ฟ”ใ™ๆง‹ๆˆใซ ๆ”นใ‚ใ€BFF ใ‚‚ๅปƒๆญขใ—ใพใ—ใŸใ€‚

ๆŠ€่ก“ใ‚นใ‚ฟใƒƒใ‚ฏ

็”จ้€” ๆŽก็”จใ—ใฆใ„ใ‚‹ใ‚‚ใฎ
ๅฎŸ่กŒ็’ฐๅขƒ Cloudflare Workers (wasm32-unknown-unknown)
API GraphQL (async-graphql 7)
ใƒ‡ใƒผใ‚ฟ ใƒ“ใƒซใƒ‰ๆ™‚ใซ WASM ใธๅŸ‹ใ‚่พผใ‚€ CSV (generated/*.csv)
ใƒ‡ใƒผใ‚ฟ็”Ÿๆˆ preprocessor crate (Rust ใฎใฟใงๅฎŸ่ฃ…)
ใƒ‡ใƒ—ใƒญใ‚ค wrangler

ใƒ‡ใƒผใ‚ฟใƒ™ใƒผใ‚นใ‚’ไฝฟใ‚ใชใ„ใฎใงใ€sqlx ใ‚‚ๆŽฅ็ถšใƒ—ใƒผใƒซใ‚‚ใ‚ใ‚Šใพใ›ใ‚“ใ€‚ๆคœ็ดขใฏใ™ในใฆใ€ ๅŸ‹ใ‚่พผใฟใƒ‡ใƒผใ‚ฟใ‹ใ‚‰ isolate ใฎไธญใซ็ต„ใฟ็ซ‹ใฆใŸใ‚คใƒณใƒกใƒขใƒช็ดขๅผ•ใซๅฏพใ—ใฆ่กŒใ„ใพใ™ใ€‚


ๅ…จไฝ“ๆง‹ๆˆ

  data/*.csv          GTFS (ZIP)        ODPT (JSON)      N02 (GeoJSON)
  ้‰„้“ใฎๆญฃๆœฌใƒ‡ใƒผใ‚ฟ      ใƒใ‚น 6 ใƒ•ใ‚ฃใƒผใƒ‰      ๆฑๆ€ฅใƒใ‚น          ๅ›ฝๅœŸๆ•ฐๅ€คๆƒ…ๅ ฑใฎ็ทš่ทฏ
      โ”‚                    โ”‚                 โ”‚                 โ”‚
      โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                           โ”‚
                 โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                 โ”‚   preprocessor     โ”‚  Rust ใฎใฟใงๅฎŸ่ฃ…ใ€‚ๅ„้ง…ๅœ่ปŠใฎ
                 โ”‚   (ใƒ“ใƒซใƒ‰ๆ™‚ใƒ„ใƒผใƒซ)  โ”‚  ็ณป็ตฑ็”Ÿๆˆใ€้ง…้–“ใฎ็ทš่ทฏใฎ้•ทใ•ใฎ
                 โ”‚                    โ”‚  ่จˆ็ฎ—ใ€GTFS ใฎ็ตฑๅˆใ‚’่กŒใ†
                 โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                           โ”‚
                    generated/*.csv     8 ใƒ†ใƒผใƒ–ใƒซ
                           โ”‚
                 โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                 โ”‚  build.rs          โ”‚  CSV ใ‚’ OUT_DIR ใซ้…็ฝฎใ—ใ€sst ใจ
                 โ”‚                    โ”‚  connections ใ‚’ๅ›บๅฎš้•ทใƒใ‚คใƒŠใƒชใซๅค‰ๆ›
                 โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                           โ”‚
                 โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                 โ”‚  worker-build      โ”‚
                 โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                           โ”‚
                        WASM โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ Cloudflare Workers
                                          โ”‚
                                     GraphQL (POST /)
                                          โ”‚
                                       TrainLCD

ใƒ‡ใƒผใ‚ฟใฏ WASM ใซๅŸ‹ใ‚่พผใพใ‚Œใ‚‹ใŸใ‚ใ€ใƒ‡ใƒผใ‚ฟใ‚’ๆ›ดๆ–ฐใ™ใ‚‹ใŸใณใซๅ†ใƒ‡ใƒ—ใƒญใ‚คใŒ ๅฟ…่ฆใงใ™ใ€‚ ่ตทๅ‹•ๆ™‚ใซใƒ‡ใƒผใ‚ฟใ‚’่ชญใฟ่พผใ‚“ใง่‡ชๅ‹•็š„ใซๅๆ˜ ใ™ใ‚‹ไป•็ต„ใฟใงใฏใ‚ใ‚Šใพใ›ใ‚“ใ€‚


ใƒฌใ‚คใƒคใƒผๆง‹้€ 

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Presentation (src/graphql/)                   โ”‚  async-graphql ใฎใƒชใ‚พใƒซใƒใจๅž‹
โ”‚   Query / ๅž‹ / enum / ใ‚นใ‚ซใƒฉใƒผ                 โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ Model (stationapi/src/model.rs)               โ”‚  API ใŒ่ฟ”ใ™ๅ€คใฎๅž‹
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ UseCase (stationapi/src/use_case/)            โ”‚  ใ‚ฏใ‚จใƒชใฎๅ‡ฆ็†ใ€
โ”‚   QueryInteractor / DTO ๅค‰ๆ›                   โ”‚  IPAใƒปTTS ใฎ็”Ÿๆˆ
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ Domain (stationapi/src/domain/)               โ”‚  ใ‚จใƒณใƒ†ใ‚ฃใƒ†ใ‚ฃใ€็ตŒ่ทฏๆŽข็ดขใ€
โ”‚   entity / repository ใƒˆใƒฌใ‚คใƒˆ / ้€Ÿๅบฆ่กจ        โ”‚  ๅˆฐ็€ๆ™‚ๅˆปใฎๆŽจๅฎšใ€ๆญฃ่ฆๅŒ–
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ Index (src/index.rs, src/repository.rs)       โ”‚  ๅŸ‹ใ‚่พผใฟใƒ‡ใƒผใ‚ฟใฎ็ดขๅผ•ใจ
โ”‚                                               โ”‚  repository ใƒˆใƒฌใ‚คใƒˆใฎๅฎŸ่ฃ…
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

stationapi crate ใฏ Domain / UseCase / Model ใ ใ‘ใ‚’ๅซใ‚€ใƒฉใ‚คใƒ–ใƒฉใƒชใงใ€ Worker ใจ preprocessor ใฎไธกๆ–นใ‹ใ‚‰ไฝฟใ‚ใ‚Œใพใ™ใ€‚wasm32 ๅ‘ใ‘ใซใƒ“ใƒซใƒ‰ใงใใชใ‘ใ‚Œใฐ ใชใ‚‰ใชใ„ใŸใ‚ใ€I/O ใ‚’ไผดใ†ไพๅญ˜ใฏ่ฟฝๅŠ ใ—ใพใ›ใ‚“ใ€‚

Domain ๅฑค (stationapi/src/domain/)

ใ‚จใƒณใƒ†ใ‚ฃใƒ†ใ‚ฃใ€ใƒชใƒใ‚ธใƒˆใƒชใฎๆŠฝ่ฑกใ€ใใ—ใฆ็ด”็ฒ‹ใช่จˆ็ฎ—ๅ‡ฆ็† (haversine ่ท้›ขใ€ ็ตŒ่ทฏๆŽข็ดขใ€ๅˆฐ็€ๆ™‚ๅˆปใฎๆŽจๅฎšใ€้€Ÿๅบฆ่กจใ€ใƒญใƒผใƒžๅญ—ใƒปIPA ๅค‰ๆ›ใ€ๆคœ็ดข็”จใฎๆญฃ่ฆๅŒ–) ใ‚’ ็ฝฎใ„ใฆใ„ใพใ™ใ€‚

UseCase ๅฑค (stationapi/src/use_case/)

QueryInteractor ใŒ repository ใƒˆใƒฌใ‚คใƒˆใ‚’้€šใ—ใฆใƒ‡ใƒผใ‚ฟใ‚’้›†ใ‚ใ€้ง…ใซ่ทฏ็ทšใƒป ไบ‹ๆฅญ่€…ใƒปๅˆ—่ปŠ็จฎๅˆฅใฎๆƒ…ๅ ฑใ‚’ไป˜ใ‘ๅŠ ใˆใพใ™ใ€‚N+1 ๅ•้กŒใ‚’้ฟใ‘ใ‚‹ใŸใ‚ใ€้–ข้€ฃใƒ‡ใƒผใ‚ฟใฏ ๅธธใซใพใจใ‚ใฆๅ–ๅพ—ใ—ใพใ™ใ€‚

DTO (use_case/dto/) ใฏใƒ‰ใƒกใ‚คใƒณใ‚จใƒณใƒ†ใ‚ฃใƒ†ใ‚ฃใ‚’ Model ใซๅค‰ๆ›ใ—ใพใ™ใ€‚IPA ใจ TTS ใ‚ปใ‚ฐใƒกใƒณใƒˆใ‚‚ใ“ใ“ใง็”Ÿๆˆใ—ใพใ™ใ€‚

Model ๅฑค (stationapi/src/model.rs)

API ใŒ่ฟ”ใ™ๅ€คใ‚’่กจใ™ๅž‹ใงใ™ใ€‚ใ‚‚ใจใ‚‚ใจใฏ prost ใŒ .proto ใ‹ใ‚‰็”Ÿๆˆใ—ใฆใ„ใŸๅž‹ ใงใ™ใ€‚gRPC ใ‚’ใ‚„ใ‚ใŸๅพŒใ‚‚ใ€IPAใƒปTTS ใฎ็”ŸๆˆใŒใ“ใฎๅž‹ใธใฎๅค‰ๆ›ๅ‡ฆ็†ใซ็ต„ใฟ่พผใพใ‚Œใฆ ใ„ใ‚‹ใŸใ‚ใ€ใƒ‰ใƒกใ‚คใƒณใ‚จใƒณใƒ†ใ‚ฃใƒ†ใ‚ฃใจ GraphQL ๅž‹ใฎ้–“ใฎๅฑคใจใ—ใฆๆฎ‹ใ—ใฆใ„ใพใ™ใ€‚

Presentation ๅฑค (src/graphql/)

async-graphql ใฎ Query ใƒชใ‚พใƒซใƒใจๅž‹ๅฎš็พฉใ‚’็ฝฎใ„ใฆใ„ใพใ™ใ€‚Model ใ‚’ GraphQL ๅž‹ใซ ๅค‰ๆ›ใ™ใ‚‹ใฎใ‚‚ใ“ใฎๅฑคใงใ™ใ€‚

Index ๅฑค (src/index.rs, src/repository.rs)

ๅŸ‹ใ‚่พผใพใ‚ŒใŸ CSV ใฏใ€isolate ใฎไธญใงๆœ€ๅˆใซๅฟ…่ฆใซใชใฃใŸใจใใซไธ€ๅบฆใ ใ‘ใƒ‘ใƒผใ‚นใ—ใ€ OnceLock ใซไฟๆŒใ—ใพใ™ใ€‚src/repository.rs ใฏ 4 ใคใฎ repository ใƒˆใƒฌใ‚คใƒˆใ‚’ๅฎŸ่ฃ…ใ—ใฆใ„ใ‚‹ใฎใงใ€ UseCase ๅฑคใ‹ใ‚‰ใฏใƒ‡ใƒผใ‚ฟใƒ™ใƒผใ‚นใ‚’ไฝฟใฃใฆใ„ใŸ้ ƒใจๅŒใ˜ใ‚คใƒณใ‚ฟใƒผใƒ•ใ‚งใƒผใ‚นใซ่ฆ‹ใˆใพใ™ใ€‚


ใƒ‡ใƒผใ‚ฟใƒ‘ใ‚คใƒ—ใƒฉใ‚คใƒณ

data/*.csv ใ‚’ใใฎใพใพ Worker ใซๆธกใ™ใจใ€ๆœฌ็•ชใจใฏ็•ฐใชใ‚‹ๆŒ™ๅ‹•ใซใชใ‚Šใพใ™ใ€‚

  • ๅˆ—่ปŠ็จฎๅˆฅใ‚’ๆŒใŸใชใ„่ทฏ็ทšใซใฏใ€ๅ„้ง…ๅœ่ปŠใฎ็ณป็ตฑใ‚’่ฃœใ†ๅฟ…่ฆใŒใ‚ใ‚Šใพใ™ (็ด„ 2,400 ่กŒใ€‚ ๆœ‰ๅŠนใช้ง…ใฎ็ด„ 21% ใŒ่ฉฒๅฝ“ใ—ใพใ™)ใ€‚ใ“ใ‚Œใ‚‰ใฎ่กŒใฏ data/*.csv ใซใฏๅซใพใ‚Œใฆ ใ„ใพใ›ใ‚“
  • ใƒใ‚นๅœใƒปใƒใ‚น่ทฏ็ทšใƒปใƒใ‚น็ณป็ตฑใฏใ€GTFS ใจ ODPT ใฎ JSON ใ‹ใ‚‰็”Ÿๆˆใ™ใ‚‹ๅฟ…่ฆใŒ ใ‚ใ‚Šใพใ™
  • ้šฃใ‚Šๅˆใ†้ง…ใฎใ‚ใ„ใ ใฎ็ทš่ทฏใฎ้•ทใ•ใฏใ€ๅ›ฝๅœŸๆ•ฐๅ€คๆƒ…ๅ ฑใฎ้‰„้“ใƒ‡ใƒผใ‚ฟใ‹ใ‚‰ๆฑ‚ใ‚ใ‚‹ๅฟ…่ฆใŒ ใ‚ใ‚Šใพใ™

ใ“ใ‚Œใ‚‰ใ‚’ๆ‹…ใ†ใฎใŒ preprocessor crate ใงใ™ใ€‚

make data     # cargo run --profile tool -p stationapi-preprocessor

ๅ‡ฆ็†ใฎๆตใ‚ŒใฏๆฌกใฎใจใŠใ‚Šใงใ™ใ€‚

  1. data/*.csv ใ‚’่ชญใฟ่พผใ‚€ (# ใงๅง‹ใพใ‚‹ๅˆ—ใฏ่ชญใฟ่พผใพใชใ„)
  2. ๅ„้ง…ๅœ่ปŠใฎ็ณป็ตฑใ‚’็”Ÿๆˆใ™ใ‚‹ (generate_virtual_local_rail_services)
  3. ๅ›ฝๅœŸๆ•ฐๅ€คๆƒ…ๅ ฑใฎ้‰„้“ใƒ‡ใƒผใ‚ฟ (N02) ใ‚’ๅ–ๅพ—ใ—ใ€้šฃใ‚Šๅˆใ†้ง…ใฎใ‚ใ„ใ ใฎ็ทš่ทฏใฎ้•ทใ•ใ‚’ ๆฑ‚ใ‚ใ‚‹ (track::generate_connectionsใ€‚้ง…้–“ใฎ็ทš่ทฏใฎ้•ทใ•)
  4. GTFS ใƒ•ใ‚ฃใƒผใƒ‰ 6 ๆœฌใ‚’ๅ–ๅพ—ใƒปๅฑ•้–‹ใ—ใฆ่ชญใฟ่พผใ‚€ (้ƒฝๅ–ถใƒใ‚นใƒป่ฅฟๆญฆใƒใ‚นใƒปไบฌ็Ž‹ใƒใ‚นใจใ€ ๆฑๆ€ฅใƒใ‚นใŒ้‹่กŒใ™ใ‚‹ๅคง็”ฐๅŒบใƒปๅ“ๅทๅŒบใƒป็›ฎ้ป’ๅŒบใฎใ‚ณใƒŸใƒฅใƒ‹ใƒ†ใ‚ฃใƒใ‚น)
  5. ๆฑๆ€ฅใƒใ‚นใฎ ODPT JSON ใ‚’่ชญใฟ่พผใ‚€ (7 ๆ—ฅ้–“ใ‚ญใƒฃใƒƒใ‚ทใƒฅใ™ใ‚‹)
  6. ใƒใ‚นใฎใƒ‡ใƒผใ‚ฟใ‚’ lines / stations / types / station_station_types ใซ็ตฑๅˆใ™ใ‚‹
  7. generated/*.csv ใซๆ›ธใๅ‡บใ™ (8 ใƒ†ใƒผใƒ–ใƒซ)

ๅ–ๅพ—ใ‚„่ชญใฟ่พผใฟใซๅคฑๆ•—ใ—ใŸใƒ•ใ‚ฃใƒผใƒ‰ใฏใ€่ญฆๅ‘Šใ‚’ๅ‡บใ—ใฆ้ฃ›ใฐใ—ใพใ™ใ€‚ใŸใ ใ—ใ€GTFS ใฎ ่ทฏ็ทšใ‚’ 1 ใคใ‚‚ๅ–ใ‚Š่พผใ‚ใชใ‹ใฃใŸๅ ดๅˆใฏใ€ใƒใ‚นใŒไธธใ”ใจๆฌ ใ‘ใŸใƒ‡ใƒผใ‚ฟใ‚’ๅ‡บใ•ใชใ„ใ‚ˆใ† ๅคฑๆ•—ใ•ใ›ใพใ™ใ€‚ใƒ‡ใƒ—ใƒญใ‚ค็”จใฎใƒฏใƒผใ‚ฏใƒ•ใƒญใƒผใงใฏใ€ใƒ•ใ‚ฃใƒผใƒ‰ใ‚’ 1 ใคใงใ‚‚้ฃ›ใฐใ—ใŸๆ™‚็‚นใง ๅคฑๆ•—ใ•ใ›ใฆใ„ใพใ™ (fail-on-missing-bus-feeds)ใ€‚

station_station_types.id ใฏใใฎใพใพๅœ่ปŠ้ †ใจใ—ใฆไฝฟใ‚ใ‚Œใ‚‹ใŸใ‚ใ€่กŒใฎ้ †ๅบใซ ๆ„ๅ‘ณใŒใ‚ใ‚Šใพใ™ใ€‚ๆ›ธใๅ‡บใ™ใจใใฏๅฟ…ใš id ใฎๆ˜‡้ †ใซไธฆในใพใ™ใ€‚

้ง…้–“ใฎ็ทš่ทฏใฎ้•ทใ•

Station.trackDistanceFromPrevious ใฏใ€่ฟ”ใ™้ง…ใฎไธฆใณใง็›ดๅ‰ใซใ‚ใ‚‹้ง…ใ‹ใ‚‰ใฎ ็ทš่ทฏใฎ้•ทใ• (ใƒกใƒผใƒˆใƒซ) ใงใ™ใ€‚trainRoute ใฎ distanceFromPrevious ใฏ้ง…ใฎๅบงๆจ™ ใฉใ†ใ—ใฎ็›ด็ทš่ท้›ขใชใฎใงใ€ใ‚ซใƒผใƒ–ใฎๅคšใ„ๅŒบ้–“ใงใฏๅฎŸ้š›ใ‚ˆใ‚Š็Ÿญใๅ‡บใพใ™ใ€‚ใ“ใกใ‚‰ใฏใใฎ ไปฃใ‚ใ‚Šใซใ€ไน—่ปŠ่ท้›ขใ‚’้›†่จˆใ™ใ‚‹ใ‚ฏใƒฉใ‚คใ‚ขใƒณใƒˆใŒไฝฟใ„ใพใ™ใ€‚

ๅ€คใฏใ€ๅ›ฝๅœŸๆ•ฐๅ€คๆƒ…ๅ ฑใฎ้‰„้“ใƒ‡ใƒผใ‚ฟ (N02ใ€ๅ›ฝๅœŸไบค้€š็œใ€CC BY 4.0) ใฎ็ทš่ทฏๅŒบ้–“ (RailroadSection) ใ‹ใ‚‰ preprocessor ใงๆฑ‚ใ‚ใ€generated/connections.csv (station_cd1 < station_cd2ใ€ๆ•ดๆ•ฐใƒกใƒผใƒˆใƒซ) ใซๆ›ธใๅ‡บใ—ใพใ™ใ€‚

  1. ใ‚ฐใƒฉใƒ•: ็ทš่ทฏๅŒบ้–“ใฎ LineString ใ‚’ใ€้ ‚็‚นใ‚’ๅบงๆจ™ (ๅฐๆ•ฐ็ฌฌ 5 ไฝ) ใงๅŒไธ€่ฆ–ใ—ใŸ ็„กๅ‘ใ‚ฐใƒฉใƒ•ใซใ™ใ‚‹ใ€‚N02-25 ใง้ ‚็‚น ็ด„ 38 ไธ‡ใ€่พบ ็ด„ 38 ไธ‡
  2. ้ง…ใฎ็ต„: API ใŒ่ฟ”ใ™้ง…ใฎไธฆใณใง้šฃใ‚Šๅˆใ†้‰„้“้ง…ใฎ็ต„ใ‚’้›†ใ‚ใ‚‹ใ€‚ไธฆใณใฏ่ทฏ็ทšใฎ (e_sort, station_cd) ้ †ใจ็ณป็ตฑใฎ station_station_types.id ้ †ใงใ€ๅปƒๆญข้ง…ใ‚’ ้™คใ„ใŸไธฆใณใจ้™คใ‹ใชใ„ไธฆใณใฎไธกๆ–นใ‚’ไฝฟใ†ใ€‚็’ฐ็Šถ้‹่ปขใฎ็ณป็ตฑใฏใ€ๆœซๅฐพ้ง…ใจๅ…ˆ้ ญ้ง…ใŒ 3km ไปฅๅ†…ใชใ‚‰็ถ™ใŽ็›ฎใฎ็ต„ใ‚‚ๅซใ‚ใ‚‹ (trainRoute ใฏ็ถ™ใŽ็›ฎใ‚’ใพใŸใ„ใงๅˆ‡ใ‚Šๅ‡บใ™ ใŸใ‚)ใ€‚ๅŒใ˜้ง…ใ‚ฐใƒซใƒผใƒ—ใฎ็ต„ (็›ด้€š้‹่ปขใฎๅขƒ็•Œ้ง…) ใฏ 0 ใซใ™ใ‚‹
  3. ้ง…ใ‚’็ทš่ทฏใธๅฏ„ใ›ใ‚‹: ้ง…ใ‹ใ‚‰ 500m ไปฅๅ†…ใฎ่พบใ‚’ใ™ในใฆๅ€™่ฃœใซใ—ใ€ ใ€Œๅฏ„ใ›ใŸ่ท้›ข ร— 2 + ็ทš่ทฏไธŠใฎ่ท้›ขใ€ใŒๆœ€ๅฐใซใชใ‚‹็ต„ใ‚’ Dijkstra ใงๆŽขใ™ใ€‚ๆœ€ใ‚‚ ่ฟ‘ใ„็ทš่ทฏใ ใ‘ใซๅฏ„ใ›ใ‚‹ใจใ€ๅคงใใช้ง…ใงๅŒใ˜ไบ‹ๆฅญ่€…ใฎๅˆฅ่ทฏ็ทš (ๆœฌ็”บใฎๅพกๅ ‚็ญ‹็ทšใจ ๅ››ใคๆฉ‹็ทšใชใฉ) ใซๅฏ„ใฃใฆไน—ๆ›้ง…ใ‚’ใพใ‚ใ‚‹้ ๅ›žใ‚ŠใŒๅ‡บใ‚‹ใŸใ‚
  4. ไบ‹ๆฅญ่€…ใง็ตžใ‚‹: ใพใšไธก้ง…ใฎ่ทฏ็ทšใฎไผš็คพใฎ็ทš่ทฏใ ใ‘ใงๆธฌใ‚‹ใ€‚N02 ใฎไบ‹ๆฅญ่€…ๅใจ ไผš็คพๅ (company_name_h ใ‹ใ‚‰ใ€Œๆ ชๅผไผš็คพใ€ใ‚’้™คใ„ใŸใ‚‚ใฎ) ใŒ้•ใ†ไผš็คพใฏ OPERATOR_ALIASES ใงๅฏพๅฟœใ•ใ›ใ‚‹ใ€‚ไป–็คพใฎ็ทš่ทฏใ‚’่ตฐใ‚‹่ทฏ็ทš (ๅŒ—้™ธๆ–ฐๅนน็ทšใฎ ไธŠ่ถŠๅฆ™้ซ˜ไปฅ่ฅฟใ€็›ธ้‰„ใƒปJR็›ด้€š็ทšใชใฉ) ใฏใใ‚Œใงใฏๅฏ„ใ›ใ‚‰ใ‚Œใชใ„ใฎใงใ€ๅ…จไบ‹ๆฅญ่€…ใฎ ็ทš่ทฏใงๆธฌใ‚Š็›ดใ™
  5. ๆ‰“ใกๅˆ‡ใ‚Šใจไธ‹้™: ็ทš่ทฏไธŠใฎ่ท้›ขใŒ็›ด็ทš่ท้›ขใฎ 3 ๅ€ใจ็›ด็ทš่ท้›ข + 5km ใฎๅคงใใ„ๆ–นใ‚’ ่ถ…ใˆใŸใ‚‰ใ€็ทšๅฝขใŒ้€”ๅˆ‡ใ‚Œใฆๅคงๅ›žใ‚Šใ—ใŸใ‚‚ใฎใจใฟใชใ—ใฆๆธฌใ‚Œใชใ‹ใฃใŸใ“ใจใซใ™ใ‚‹ (ๅฎŸๅœจใฎๆœ€ๅคงใฏๆœจๆฌก็ทšใฎๅ‡บ้›ฒๅ‚ๆ นใ€œไธ‰ไบ•้‡ŽๅŽŸใฎ็ด„ 3.6 ๅ€)ใ€‚็ทš่ทฏใฎ้•ทใ•ใฏ็›ด็ทš่ท้›ขใ‚ˆใ‚Š ็Ÿญใใชใ‚‰ใชใ„ใฎใงใ€้ง…ใฎๅบงๆจ™ใฎใšใ‚Œใง็Ÿญใๅ‡บใŸๅ€คใฏ็›ด็ทš่ท้›ขใงๆŠ‘ใˆใ‚‹

2026 ๅนด 9 ๆœˆๆ™‚็‚นใฎใƒ‡ใƒผใ‚ฟใงใฏใ€็ด„ 11,000 ็ต„ใฎใ†ใก็ด„ 10,400 ็ต„ใ‚’่จˆ็ฎ—ใงใใ€ ๆธฌใ‚Œใชใ‹ใฃใŸ็ด„ 290 ็ต„ใฎใปใจใ‚“ใฉใฏ N02 ใซ็ทš่ทฏใŒๆฎ‹ใฃใฆใ„ใชใ„ๅปƒ็ทšใฎ้ง…ใงใ—ใŸ (ๅ–ถๆฅญไธญใฎ้ง…ใฉใ†ใ—ใฏ 2 ็ต„)ใ€‚ๅฎŸใ‚ญใƒญใจๆฏ”ในใ‚‹ใจใ€ๆฑๆตท้“ใƒปๅฑฑ้™ฝๆ–ฐๅนน็ทšใฎๆฑไบฌใ€œๅšๅคšใŒ 1,066km (ๅฎŸใ‚ญใƒญ 1,069km)ใ€็Ÿณๅ‹็ทšใฎใƒˆใƒžใƒ ใ€œๆ–ฐๅพ—ใŒ 33.8km (ๅ–ถๆฅญใ‚ญใƒญ 33.8km) ใงใ™ใ€‚ preprocessor ใฎๅฎŸ่กŒใฏ 1 ็ง’ใปใฉใงใ™ใ€‚

data/8!connections.csv ใซๆ›ธใ„ใŸ่กŒใฏ่จˆ็ฎ—็ตๆžœใ‚ˆใ‚Šๅ„ชๅ…ˆใ—ใพใ™ใ€‚N02 ใฎ็ทšๅฝขใ‚„้ง…ใฎ ๅบงๆจ™ใฎใšใ‚Œใงๅ€คใŒๅฎŸ้š›ใจๅˆใ‚ใชใ„ๅŒบ้–“ใฏใ€ใ“ใ“ใง็›ดใ—ใพใ™ใ€‚

Worker ใงใฏ build.rs ใŒ 1 ่กŒ = i32 3 ใคใฎๅ›บๅฎš้•ทใƒใ‚คใƒŠใƒช (connections.binใ€ ็ด„ 130KB) ใซๅค‰ๆ›ใ—ใ€็ต„ใฎๆ˜‡้ †ใซไธฆในใพใ™ใ€‚ใƒฉใƒณใ‚ฟใ‚คใƒ ใฏ็ดขๅผ•ใ‚’ไฝœใ‚‰ใšใซไบŒๅˆ†ๆŽข็ดขใง ๅผ•ใใฎใงใ€ใ‚ณใƒผใƒซใƒ‰ใ‚นใ‚ฟใƒผใƒˆใฎ่ฒป็”จใฏๅข—ใˆใพใ›ใ‚“ใ€‚ๅ€คใ‚’ๅŸ‹ใ‚ใ‚‹ใฎใฏใ€ไธฆใณใ‚’่ฟ”ใ™ lineStationsใƒปlineGroupStationsใƒปtrainRouteใƒปstations(ids) ใ ใ‘ใงใ™ (ไป–ใฎๅ•ใ„ๅˆใ‚ใ›ใงใฏ็›ดๅ‰ใฎ้ง…ใŒ็„กใ„ใฎใง null)ใ€‚stations(ids) ใฏ้ง…ใ‚’ๆŒ‡ๅฎšใ—ใŸ ID ใฎ้ †ใซ่ฟ”ใ™ใฎใงใ€็ตŒ่ทฏใฎ้ง…ใ‚’้ †ใซๆธกใ™ใ‚ฏใƒฉใ‚คใ‚ขใƒณใƒˆ (MobileApp ใฎ sids ๅฝขๅผใฎ ใƒ‡ใ‚ฃใƒผใƒ—ใƒชใƒณใ‚ฏ) ใฏใ€ใใฎ้ †ใง้šฃใ‚Šๅˆใ†็ต„ใฎ้•ทใ•ใ‚’ๅ—ใ‘ๅ–ใ‚Œใพใ™ใ€‚้€”ไธญใฎ้ง…ใ‚’็œใ„ใŸ ไธฆใณใชใฉใ€็ทš่ทฏใฎใƒ‡ใƒผใ‚ฟไธŠใง้šฃใ‚Šๅˆใ‚ใชใ„็ต„ใฏ null ใงใ™ใ€‚trainRoute ใ‚’ legs ใงๅ‘ผใ‚“ใ ๅ ดๅˆใฏใ€ๅ„ๅŒบ้–“ใฎ ๅ…ˆ้ ญใฎ้ง…ใ‚‚ null ใซใชใ‚Šใพใ™ใ€‚

N02 ใฏ data/N02-25/ ใซใ‚ญใƒฃใƒƒใ‚ทใƒฅใ—ใพใ™ (git ็ฎก็†ๅค–ใ€‚CI ใงใฏ actions/cache ใง ไฟๅญ˜ใ—ใพใ™)ใ€‚ๅ–ๅพ—ใงใใชใ‹ใฃใŸๅ ดๅˆใ€preprocessor ใฏๅคฑๆ•—ใ—ใพใ™ใ€‚็‰ˆใ‚’ไธŠใ’ใ‚‹ใจใใฏ preprocessor/src/track/mod.rs ใฎ URL ใจใ‚ญใƒฃใƒƒใ‚ทใƒฅๅ…ˆใ€.github/actions/build-worker/action.yml ใฎใ‚ญใƒฃใƒƒใ‚ทใƒฅใ‚’ๆƒใˆใฆๅค‰ใˆใ€ๆธฌใ‚Œใชใ‹ใฃใŸ็ต„ใฎไปถๆ•ฐใ‚’็ขบใ‹ใ‚ใฆใใ ใ•ใ„ใ€‚

ใƒใ‚นใฎใ‚ณใƒผใƒ‰็”Ÿๆˆ

ใƒใ‚น็”ฑๆฅใฎใƒฌใ‚ณใƒผใƒ‰ใซใฏใ€FNV-1a ใƒใƒƒใ‚ทใƒฅใ‚’ไฝฟใฃใฆใ€้‰„้“ใจ้‡ใชใ‚‰ใชใ„ๅ€คๅŸŸใฎ ใ‚ณใƒผใƒ‰ใ‚’ๆฑบๅฎš็š„ใซๅ‰ฒใ‚Šๅฝ“ใฆใพใ™ใ€‚ๅฎŸ่กŒใ™ใ‚‹ใŸใณใซๅŒใ˜ๅ€คใซใชใ‚‰ใชใ‘ใ‚Œใฐใชใ‚‰ใชใ„ ใฎใงใ€DefaultHasher ใฏไฝฟใ„ใพใ›ใ‚“ใ€‚

ๅฏพ่ฑก ๅ€คๅŸŸ ใƒใƒƒใ‚ทใƒฅใฎๅ…ฅๅŠ›
line_cd 100,000,000ใ€œ (1,000 ไธ‡ไปถๅˆ†) route_id
station_cd 200,000,000ใ€œ (1 ๅ„„ไปถๅˆ†) (stop_id, route_id)
station_g_cd 200,000,000ใ€œ (1 ๅ„„ไปถๅˆ†) stop_id (ไธŠไธ‹็ทšใชใฉใฎใƒใƒผใƒซใ‚’ใพใจใ‚ใŸไปฃ่กจใฎๅœ็•™ๆ‰€)
type_cd 100,000,000ใ€œ (1 ๅ„„ไปถๅˆ†) (route_id, shape_id)
line_group_cd 100,000,000ใ€œ (1 ๅ„„ไปถๅˆ†) (route_id, shape_id)

ใƒใƒƒใ‚ทใƒฅใชใฎใงใ€ๅˆฅใ€…ใฎๅ…ฅๅŠ›ใŒๅŒใ˜ๅ€คใซใชใ‚‹ใ“ใจใŒใ‚ใ‚Šใพใ™ (ๅฎŸใƒ‡ใƒผใ‚ฟใงใ‚‚ใ€้ƒฝๅ–ถ ใƒใ‚นใจ่ฅฟๆญฆใƒใ‚นใฎๅœ็•™ๆ‰€ใง station_cd ใŒ 1 ไปถ่ก็ชใ—ใฆใ„ใพใ—ใŸ)ใ€‚station_g_cd ไปฅๅค–ใฏใ€่ก็ชใ—ใŸใ‚‰ๆฌกใฎ็ฉบใๅ€คใซใšใ‚‰ใ—ใฆๅฟ…ใšไธ€ๆ„ใซใ—ใพใ™ใ€‚station_g_cd ใฏ ๅœ็•™ๆ‰€ใ‚’ใพใจใ‚ใ‚‹ใŸใ‚ใฎใ‚ญใƒผใงไธ€ๆ„ใงใ‚ใ‚‹ๅฟ…่ฆใŒใชใ„ใŸใ‚ใ€ใšใ‚‰ใ—ใพใ›ใ‚“ใ€‚

็’ฐๅขƒๅค‰ๆ•ฐ

ๅค‰ๆ•ฐ ๅŠนๆžœ
ODPT_ACCESS_TOKEN ้ƒฝๅ–ถใƒใ‚นไปฅๅค–ใฎใƒ•ใ‚ฃใƒผใƒ‰ใฎๅ–ๅพ—ใซๅฟ…่ฆใ€‚ๆœช่จญๅฎšใฎๅ ดๅˆใฏใ€ๅฑ•้–‹ๆธˆใฟใฎ GTFS (data/*-GTFS/) ใจ 7 ๆ—ฅไปฅๅ†…ใฎ ODPT JSON ใฎใ‚ญใƒฃใƒƒใ‚ทใƒฅใ ใ‘ใ‚’ไฝฟใ„ใ€ใฉใกใ‚‰ใ‚‚ใชใ„ใƒ•ใ‚ฃใƒผใƒ‰ใฏ่ญฆๅ‘Šใ‚’ๅ‡บใ—ใฆ้ฃ›ใฐใ™
DISABLE_BUS_FEATURE true (ใพใŸใฏ 1) ใซใ™ใ‚‹ใจใƒใ‚นใ‚’ๅ–ใ‚Š่พผใพใชใ„ (้‰„้“ใฎใฟ)

ใ‚คใƒณใƒกใƒขใƒช็ดขๅผ•

PostgreSQL ใฎใ‚ฏใ‚จใƒชใฏใ€ๆฌกใฎใ‚ˆใ†ใซ็ฝฎใๆ›ใˆใฆใ„ใพใ™ใ€‚

PostgreSQL Worker
point(lat,lon) <-> point() ใ‚ฐใƒชใƒƒใƒ‰็ดขๅผ• (Grid) ใงๆŽข็ดขๅŠๅพ„ใฎๅ†…ๅดใ ใ‘ใ‚’่ชฟในใ€haversine ใง่ท้›ขใ‚’่จˆ็ฎ—ใ™ใ‚‹
pg_trgm ใฎ GIN ใ‚คใƒณใƒ‡ใƒƒใ‚ฏใ‚น ๅ…จไปถ่ตฐๆŸปใจ contains()
station_station_types ใฎ JOIN HashMap ใซใ‚ˆใ‚‹็ดขๅผ•
connections ใฎๅ‚็…ง ไธฆในๆธˆใฟใฎๅ›บๅฎš้•ทใƒใ‚คใƒŠใƒชใ‚’ไบŒๅˆ†ๆŽข็ดข (้ง…้–“ใฎ็ทš่ทฏใฎ้•ทใ•)

ๅบงๆจ™ใซใ‚ˆใ‚‹ๆคœ็ดข

ๅบงๆจ™ใซใ‚ˆใ‚‹ๆคœ็ดขใฏใ€src/index.rs ใฎ 2 ใคใฎ้–ขๆ•ฐใง่กŒใ„ใพใ™ใ€‚

  • nearest: ่ฟ‘ใ„้ †ใซ limit ไปถใ‚’่ฟ”ใ—ใพใ™ (stationsNearby)
  • within_radius: ๅŠๅพ„ๅ†…ใฎใ‚‚ใฎใ‚’ใ™ในใฆ่ฟ”ใ—ใพใ™ (้‰„้“้ง…ใซ่ฟ‘ๅ‚ใฎใƒใ‚นๅœใ‚’ ไป˜ใ‘ใ‚‹ใจใ)

ใฉใกใ‚‰ใ‚‚ๅ…จไปถใฏ่ตฐๆŸปใ›ใšใ€้‰„้“ใจใƒใ‚นใงๅˆฅใ€…ใซไฝœใฃใŸใ‚ฐใƒชใƒƒใƒ‰็ดขๅผ• (Grid) ใ‚’ ไฝฟใ„ใพใ™ใ€‚ใ‚ฐใƒชใƒƒใƒ‰ใฏ็ทฏๅบฆ็ตŒๅบฆ 0.05ยฐ (็ด„ 5.5km) ๅ››ๆ–นใฎใƒžใ‚นใงใ€ใƒžใ‚นใ”ใจใฎ้ง…ใ‚’ CSR ๅฝขๅผใงๆŒใกใพใ™ใ€‚

nearest ใฏๅŠๅพ„ 1km ใ‹ใ‚‰ๆŽขใ—ๅง‹ใ‚ใ€ๅŠๅพ„ใฎๅ†…ๅดใซ limit ไปถใใ‚ใ‚ใชใ‘ใ‚Œใฐ ๅŠๅพ„ใ‚’ 4 ๅ€ใšใคๅบƒใ’ใพใ™ใ€‚ๅŠๅพ„ใฎๅ†…ๅดใซ limit ไปถใใ‚ใˆใฐใ€ใใฎๅค–ๅดใฎ้ง…ใŒ ไธŠไฝ limit ไปถใซๅ…ฅใ‚‹ใ“ใจใฏใชใ„ใŸใ‚ใงใ™ใ€‚็ดขๅผ•ใฎ็ฏ„ๅ›ฒๅ…จไฝ“ใ‚’่ฆ†ใฃใฆใ‚‚่ถณใ‚Šใชใ„ ๅ ดๅˆใฏใ€ใใฎๆ™‚็‚นใฎ็ตๆžœใ‚’่ฟ”ใ—ใพใ™ใ€‚transportType ใ‚’็œ็•ฅใ—ใŸๅ ดๅˆใฏใ€็งป่กŒๅ‰ใฎ SQL (ORDER BY transport_type, distance) ใจๅŒใ˜ใใ€้‰„้“้ง…ใ‚’ๅ…ˆใซใ€ใƒใ‚นๅœใ‚’ ๅพŒใซไธฆในใพใ™ใ€‚ไปถๆ•ฐใฎไธŠ้™ใฏไธฆในใŸๅพŒใฎๅ…จไฝ“ใซใ‹ใ‹ใ‚‹ใฎใงใ€ใพใš้‰„้“้ง…ใงๅŸ‹ใ‚ใ€ ๆฎ‹ใ‚Šใฎๆž ใฎๅˆ†ใ ใ‘ใƒใ‚นๅœใ‚’ๆŽขใ—ใพใ™ใ€‚่ท้›ขใŒๅŒใ˜ๅ ดๅˆใฏ station_cd ใฎ้ †ใซ ไธฆในใ€็ตๆžœใŒไธๅฎ‰ๅฎšใชใ‚ฝใƒผใƒˆใซๅทฆๅณใ•ใ‚Œใชใ„ใ‚ˆใ†ใซใ—ใฆใ„ใพใ™ใ€‚

้‰„้“้ง…ใ‚’่ฟ”ใ™ใ‚ฏใ‚จใƒชใงใฏใ€่ฟ‘ๅ‚ใฎใƒใ‚นๅœใ‚’ไป˜ใ‘ใ‚‹ใŸใ‚ใซ้ง…ใ”ใจใซๅบงๆจ™ๆคœ็ดขใŒ ่ตฐใ‚Šใพใ™ใ€‚ๅบงๆจ™ใซใ‚ˆใ‚‹ๆคœ็ดขใ‚’่ฟฝๅŠ ใ™ใ‚‹ใจใใฏใ€ๅ…จไปถ่ตฐๆŸปใงใฏใชใใ“ใฎใ‚ฐใƒชใƒƒใƒ‰ ็ดขๅผ•ใ‚’ไฝฟใฃใฆใใ ใ•ใ„ใ€‚

ๅๅ‰ใซใ‚ˆใ‚‹ๆคœ็ดข

ๅๅ‰ใซใ‚ˆใ‚‹ๆคœ็ดข (search_by_name) ใฏใ€้ง…ๅใƒป่ชญใฟใƒปใƒญใƒผใƒžๅญ—ใƒปไธญๅ›ฝ่ชžใƒป้Ÿ“ๅ›ฝ่ชžใฎ ใ„ใšใ‚Œใ‹ใซ้ƒจๅˆ†ไธ€่‡ดใ™ใ‚‹้ง…ใ‚’ใ€ๅ…จไปถ่ตฐๆŸปใงๆŽขใ—ใพใ™ใ€‚pg_trgm ใฏ LIKE '%...%' ใ‚’้ซ˜้€ŸๅŒ–ใ™ใ‚‹ใŸใ‚ใฎใ‚คใƒณใƒ‡ใƒƒใ‚ฏใ‚นใงใ€้กžไผผๅบฆๆคœ็ดขใซไฝฟใฃใฆใ„ใŸ ใ‚ใ‘ใงใฏใ‚ใ‚Šใพใ›ใ‚“ใ€‚ใใฎใŸใ‚ contains() ใงใ‚‚่ซ–็†็š„ใซๅŒใ˜็ตๆžœใซใชใ‚Šใพใ™ใ€‚ ๆญฃ่ฆๅŒ–ใซใฏ domain ๅฑคใฎ normalize_for_search ใ‚’ใใฎใพใพไฝฟใ„ใพใ™ใ€‚

ๅœ่ปŠ้ง… (station_station_types)

station_station_types.csv ใฏ้‰„้“ใ ใ‘ใงใ‚‚ 4 ไธ‡่กŒใ‚’่ถ…ใˆ (data/ ใง 41,976 ่กŒ)ใ€ใƒใ‚นใ‚’ๅซใ‚ใ‚‹ใจใ•ใ‚‰ใซๅข—ใˆใพใ™ใ€‚CSV ใฎใƒ‘ใƒผใ‚นใŒใ‚ณใƒผใƒซใƒ‰ใ‚นใ‚ฟใƒผใƒˆๆ™‚้–“ใฎ ๅคงๅŠใ‚’ๅ ใ‚ใฆใ„ใŸใฎใงใ€build.rs ใง 1 ่กŒใ‚’ i32 4 ใค (station_cdใ€ type_cdใ€line_group_cdใ€pass) ใฎๅ›บๅฎš้•ทใƒใ‚คใƒŠใƒช (sst.bin) ใซไบ‹ๅ‰ๅค‰ๆ›ใ—ใฆ ใ„ใพใ™ใ€‚id ใฏไฟๅญ˜ใ›ใšใ€่ชญใฟ่พผใฟๆ™‚ใซ่กŒ้ †ใง 1 ใ‹ใ‚‰ๆŒฏใ‚Š็›ดใ—ใพใ™ใ€‚


ไน—ๆ›็ตŒ่ทฏๆŽข็ดข

connectedRoutes ใฏใ€ไน—ๆ›ๆกˆๅ†…ใ‚ขใƒ—ใƒชใจๅŒใ˜ใ‚ˆใ†ใซใ€ไน—ๆ›ใ‚’ๅซใ‚€็ตŒ่ทฏใ‚’่‡ชๅ‹•ใง ๆŽขใ—ใพใ™ใ€‚routes / routeTypes ใŒๅ‡บ็™บ้ง…ใจๅˆฐ็€้ง…ใฎไธกๆ–นใซๅœ่ปŠใ™ใ‚‹็ณป็ตฑใ ใ‘ใ‚’ ่ฟ”ใ™ใฎใซๅฏพใ—ใ€connectedRoutes ใฏ่ค‡ๆ•ฐใฎ็ณป็ตฑใ‚’ไน—ใ‚Š็ถ™ใ็ตŒ่ทฏใ‚’่ฟ”ใ—ใพใ™ใ€‚ ๅฎŸ่ฃ…ใฏ stationapi/src/domain/route_search.rs (็ด”็ฒ‹ใชใƒญใ‚ธใƒƒใ‚ฏ) ใซใ‚ใ‚Šใพใ™ใ€‚ ๆŽข็ดขใ‚ขใƒซใ‚ดใƒชใ‚บใƒ ใฎๅ†…้ƒจ่จญ่จˆ (ใƒ‡ใƒผใ‚ฟๆง‹้€ ใ€่ตฐๆŸปใฎ่จˆ็ฎ—ๅผใ€ๆžๅˆˆใ‚Šใ€ไปฃๆ›ฟ็ตŒ่ทฏใฎ ็”Ÿๆˆใ€ๆฑบๅฎšๆ€ง) ใฏ ไน—ๆ›็ตŒ่ทฏๆŽข็ดข (RAPTOR) ใฎ่จญ่จˆ ใซ ใพใจใ‚ใฆใ„ใพใ™ใ€‚

ใƒฌใ‚นใƒใƒณใ‚นใฎๅฝข

ใ‚ขใƒ—ใƒชใฏๅˆ—่ปŠ (็ณป็ตฑ) 1 ๆœฌใ”ใจใซใ€ใ€Œ็จฎๅˆฅใ‚’้ธใถ โ†’ lineGroupStations ใง็ณป็ตฑ ๅ…จไฝ“ใฎ้ง…ใ‚’ๅ–ๅพ—ใ™ใ‚‹ โ†’ LCD ่กจ็คบใ‚’ๅ‹•ใ‹ใ™ใ€ใจใ„ใ†ๆตใ‚Œใงๅ‹•ไฝœใ—ใพใ™ใ€‚ไน—ๆ›็ตŒ่ทฏใ‚‚ ใ“ใฎๆตใ‚Œใงๆ‰ฑใˆใ‚‹ใ‚ˆใ†ใ€็ตŒ่ทฏใฏๅŒบ้–“ (legs) ใฎใƒชใ‚นใƒˆใจใ—ใฆ่ฟ”ใ—ใพใ™ใ€‚ๅ„ๅŒบ้–“ใฏใ€ ใใฎๅŒบ้–“ใงไน—่ปŠใงใใ‚‹็จฎๅˆฅ (trainTypesใ€‚routeTypes ใจๅŒใ˜ๅฝขใงใ€ๅฎŸๅœจใ™ใ‚‹ groupId ใ‚’ๆŒใค) ใจใ€ไน—่ปŠ้ง…ใƒป้™่ปŠ้ง… (Station) ใ‚’ๆŒใกใพใ™ใ€‚ไน—่ปŠ้ง…ใƒป้™่ปŠ้ง…ใฏ ๆŽข็ดขใงไฝฟใฃใŸ็ณป็ตฑใŒ่ตฐใ‚‹่ทฏ็ทšใฎ้ง…ใชใฎใงใ€ไน—ๆ›้ง…ใงใฏใ€ๅ‰ใฎๅŒบ้–“ใฎ้™่ปŠ้ง…ใจๆฌกใฎ ๅŒบ้–“ใฎไน—่ปŠ้ง…ใŒใ€ๅŒใ˜้ง…ใ‚ฐใƒซใƒผใƒ—ใซๅฑžใ™ใ‚‹ๅˆฅใฎ้ง…ใซใชใ‚‹ใ“ใจใŒใ‚ใ‚Šใพใ™ (ไพ‹: ไธธใƒŽๅ†…็ทšใฎ่ตคๅ‚่ฆ‹้™„ โ†’ ๅŠ่”ต้–€็ทšใฎๆฐธ็”ฐ็”บ)ใ€‚

connectedRoutes(fromStationGroupId: Int!, toStationGroupId: Int!, viaLineId: Int, sortBy: ConnectedRouteSort): [ConnectedRoute!]!

enum ConnectedRouteSort { Recommended  ArrivalTime  TransferCount }

type ConnectedRoute { legs: [RouteLeg!] }
type RouteLeg { trainTypes: [TrainType!]  fromStation: Station  toStation: Station  stationGroupIds: [Int!] }

stationGroupIds ใฏใ€ไน—่ปŠ้ง…ใ‹ใ‚‰้™่ปŠ้ง…ใพใงใฎ้ง…ใ‚ฐใƒซใƒผใƒ— ID ใ‚’้€ฒ่กŒ้ †ใซไธฆในใŸ ใ‚‚ใฎ (้€š้Ž้ง…ใ‚’ๅซใ‚€) ใงใ€ๆŽข็ดขใงไฝฟใฃใŸ็ณป็ตฑใฎ้ง…ใฎไธฆใณใ‚’ใใฎใพใพ่ฟ”ใ—ใพใ™ใ€‚ ้ง… ID ใงใฏใชใ้ง…ใ‚ฐใƒซใƒผใƒ— ID ใซใ—ใฆใ„ใ‚‹ใฎใฏใ€ใ‚ขใƒ—ใƒชใŒๅŒบ้–“ใ”ใจใซ้ธใถ็จฎๅˆฅ (ๆ—ขๅฎšใฏๅ„้ง…ๅœ่ปŠ) ใŒใ€ๆŽข็ดขใงไฝฟใฃใŸ็ณป็ตฑใจใฏๅˆฅใฎ่ทฏ็ทšใ‚’่ตฐใ‚‹ใ“ใจใŒใ‚ใ‚‹ใŸใ‚ใงใ™ใ€‚ ้ง… ID ใฏ่ทฏ็ทšใ”ใจใซ็•ฐใชใ‚Šใพใ™ใŒใ€้ง…ใ‚ฐใƒซใƒผใƒ— ID ใชใ‚‰ใฉใฎ็จฎๅˆฅใฎ้ง…ใƒชใ‚นใƒˆใจใ‚‚ ็…งๅˆใงใใพใ™ใ€‚ใ‚ขใƒ—ใƒชใฏใ“ใฎไธฆใณใซๆฒฟใฃใฆใ€้ธใ‚“ใ ็จฎๅˆฅใฎ้ง…ใƒชใ‚นใƒˆใ‹ใ‚‰้ง…ใ‚’ ๅ–ใ‚Šๅ‡บใ™ใฎใงใ€็’ฐ็Šถ็ทšใงใฉใกใ‚‰ๅ›žใ‚Šใซใ™ใ‚‹ใ‹ใ‚’ใ‚ขใƒ—ใƒชๅดใงๅˆคๆ–ญใ™ใ‚‹ๅฟ…่ฆใฏ ใ‚ใ‚Šใพใ›ใ‚“ใ€‚ๅŒใ˜้ง…ใ‚ฐใƒซใƒผใƒ—ใŒ 2 ๅ›ž็พใ‚Œใ‚‹็ณป็ตฑ (ๅคงๆฑŸๆˆธ็ทšใฎ้ƒฝๅบๅ‰) ใงใฏใ€ ็›ดๅ‰ใซๅ–ใ‚Šๅ‡บใ—ใŸ้ง…ใซ้šฃๆŽฅใ™ใ‚‹ใปใ†ใ‚’้ธใณใพใ™ใ€‚

ๆŽข็ดขใงใฏใ€ๅœ่ปŠ้ง…ใŒๅŒใ˜ไธฆ่กŒ็จฎๅˆฅ (ไธญๅคฎ็ทšใฎๅฟซ้€Ÿใจ้€šๅ‹คๅฟซ้€Ÿใชใฉ) ใ‚’ 1 ใคใฎ็ตŒ่ทฏใซ ใพใจใ‚ใ€ไปฃๆ›ฟ็ตŒ่ทฏใ‚’ๅ†ๆŽข็ดขใ™ใ‚‹ใจใใ‚‚ใใ‚Œใ‚‰ใ‚’ใพใจใ‚ใฆ้™คๅค–ใ—ใพใ™ใ€‚ใใฎใพใพใงใฏ ไธฆ่กŒ็จฎๅˆฅใŒใƒฌใ‚นใƒใƒณใ‚นใซไธ€ๅบฆใ‚‚็พใ‚Œใšใ€็จฎๅˆฅไธ€่ฆง (TrainTypeListModal) ใ‚’่กจ็คบใ—ใฆ ๅ„้ง…ๅœ่ปŠใ‚’ๆ—ขๅฎšใง้ธใถใจใ„ใ†ใ€็พๅœจใฎใ‚ขใƒ—ใƒชใฎๆŒ™ๅ‹•ใ‚’็ถญๆŒใงใใพใ›ใ‚“ใ€‚ใใ“ใง trainTypes ใซใฏใ€ใใฎๅŒบ้–“ใงไน—่ปŠใงใใ‚‹็จฎๅˆฅใ‚’ใ™ในใฆ่ฟ”ใ—ใพใ™ใ€‚ไธญ่บซใฏ routeTypes(ไน—่ปŠ้ง…ใ‚ฐใƒซใƒผใƒ—, ้™่ปŠ้ง…ใ‚ฐใƒซใƒผใƒ—, ้™่ปŠ้ง…ใฎ่ทฏ็ทš) ใฎ็ตๆžœใจๅŒไธ€ใงใ€ ๅœ่ปŠ้ง…ใŒๅŒใ˜็จฎๅˆฅใฎ้›†็ด„ใ€่ทฏ็ทšใฎไป˜ไธŽใ€ไธฆใณ้ †ใ‚‚ routeTypes ใจๅŒใ˜ใงใ™ (ๅŒใ˜ ้–ขๆ•ฐใ‚’ๅ‘ผใณๅ‡บใ—ใฆใ„ใพใ™)ใ€‚ๆŽข็ดขใŒ้ธใ‚“ใ ไปฃ่กจใฎ็จฎๅˆฅใ€ๆŽจๅฎšๆ‰€่ฆๆ™‚้–“ใ€ไน—ๆ›ๅ›žๆ•ฐใฏ ไธฆในๆ›ฟใˆใซใ ใ‘ไฝฟใ„ใพใ™ใ€‚ใ‚ขใƒ—ใƒชใงใฏไฝฟใ‚ใชใ„ใŸใ‚ใ€API ใงใฏ่ฟ”ใ—ใพใ›ใ‚“ใ€‚

viaLineId ใฏ routeTypes ใจๅŒๆง˜ใซใ€ๆคœ็ดข็ตๆžœใงใ‚ฟใƒƒใƒ—ใ—ใŸ้ง…ใฎ่ทฏ็ทšใ‚’ๆŒ‡ใ—ใพใ™ใ€‚ ๆŒ‡ๅฎšใ™ใ‚‹ใจใ€็›ฎ็š„ๅœฐใซใใฎ่ทฏ็ทšใฎ้ง…ใงๅˆฐ็€ใ™ใ‚‹็ตŒ่ทฏ (ๆœ€ๅพŒใฎๅŒบ้–“ใŒใใฎ่ทฏ็ทšใ‚’่ตฐใ‚‹ ็ตŒ่ทฏ) ใ ใ‘ใซ็ตžใ‚Š่พผใฟใพใ™ใ€‚

sortBy ใฏ็ตŒ่ทฏใฎไธฆใณ้ †ใงใ™ใ€‚ๆŽจๅฎšๆ‰€่ฆๆ™‚้–“ใจไน—ๆ›ๅ›žๆ•ฐใฏ API ใง่ฟ”ใ•ใชใ„ใฎใงใ€ ไธฆในๆ›ฟใˆใฏใ‚ตใƒผใƒใƒผๅดใง่กŒใ„ใพใ™ใ€‚ใฉใ‚Œใ‚’้ธใ‚“ใงใ‚‚่ฟ”ใ™็ตŒ่ทฏใฎ้›†ๅˆใฏๅค‰ใ‚ใ‚‰ใšใ€ ไธฆใณใ ใ‘ใŒๅค‰ใ‚ใ‚Šใพใ™ใ€‚

sortBy ไธฆใณ
Recommended (็œ็•ฅๆ™‚) ใŠใ™ใ™ใ‚้ †ใ€‚ใ€Œ่ฉ•ไพกๅ€ค (ๆœ€ๅˆใฎๅˆ—่ปŠใฎๅพ…ใกๆ™‚้–“ใ‚’ๅซใ‚€ๆ‰€่ฆๆ™‚้–“ใฎ่ฆ‹่พผใฟ) + ไน—ๆ› 1 ๅ›žใซใคใ 5 ๅˆ†ใ€ใฎๅฐใ•ใ„้ † (ๅพŒ่ฟฐ)
ArrivalTime ๅˆฐ็€ใฎๆ—ฉใ„้ †ใ€‚ๅŒใ˜ใชใ‚‰ไน—ๆ›ใฎๅฐ‘ใชใ„้ †
TransferCount ไน—ๆ›ใฎๅฐ‘ใชใ„้ †ใ€‚ๅŒใ˜ใชใ‚‰ๅˆฐ็€ใฎๆ—ฉใ„้ †

ๅˆฐ็€ใฎๆ—ฉใ„้ †ใฏใ€estimateArrivalTimes ใจๅŒใ˜ๅฎš็พฉใฎๆ‰€่ฆๆ™‚้–“ (ๆœ€ๅˆใฎๅˆ—่ปŠใฎ ๅพ…ใกๆ™‚้–“ใฏๅซใ‚ใšใ€ไน—ๆ›ใ”ใจใซๅพ’ๆญฉๆ™‚้–“ใจไน—ๆ›ๅ…ˆใฎๅพ…ใกๆ™‚้–“ใ‚’ๅŠ ใˆใ‚‹) ใงไธฆในใพใ™ใ€‚ ใŸใ ใ—ใ€ไธฆในๆ›ฟใˆใซไฝฟใ†ใฎใฏๆŽข็ดขใŒ้ธใ‚“ใ ไปฃ่กจใฎ็จฎๅˆฅใงใฎ่ฆ‹่พผใฟใงใ™ใ€‚ใ‚ขใƒ—ใƒชใŒ ๅŒบ้–“ใ”ใจใซๅˆฅใฎ็จฎๅˆฅ (ๆ—ขๅฎšใฎๅ„้ง…ๅœ่ปŠใชใฉ) ใ‚’้ธใ‚“ใง estimateArrivalTimes ใง ๆฑ‚ใ‚ใŸๅˆฐ็€่ฆ‹่พผใฟใจใฏใ€ไธฆใณใŒไธ€่‡ดใ—ใชใ„ใ“ใจใŒใ‚ใ‚Šใพใ™ใ€‚ใ‚ญใƒผใŒๅŒใ˜็ตŒ่ทฏๅŒๅฃซใฏใ€ ใŠใ™ใ™ใ‚้ †ใฎใพใพไธฆใณใพใ™ใ€‚

็ณป็ตฑ็ถฒ

็ณป็ตฑ (line_group_cd) ใ”ใจใฎๅœ่ปŠ้ง…ใฎไธฆใณใ‚’ 1 ใคใฎใƒ‘ใ‚ฟใƒผใƒณใจใ—ใ€้ง…ใ‚ฐใƒซใƒผใƒ— (station_g_cd) ใ‚’ไน—ๆ›ใฎใƒŽใƒผใƒ‰ใจใ—ใพใ™ใ€‚้ง…้–“ใฎๆ‰€่ฆๆ™‚้–“ใซใฏ arrival_estimation ใฎๆŽจๅฎšๅ€คใ‚’ไฝฟใ„ใพใ™ใ€‚็’ฐ็Šถ็ทšใฏ 2 ๅ‘จๅˆ†ใซๅฑ•้–‹ใ—ใ€็ถ™ใŽ็›ฎใ‚’ ใพใŸใไน—่ปŠใ‚‚ๅŒใ˜้…ๅˆ—ใง่จˆ็ฎ—ใงใใ‚‹ใ‚ˆใ†ใซใ—ใฆใ„ใพใ™ใ€‚ๅฏพ่ฑกใฏ้‰„้“ใ ใ‘ใงใ€ใƒใ‚นใฏ ๅซใฟใพใ›ใ‚“ใ€‚

็ถฒใฎๆง‹็ฏ‰ใซใฏๅ…จ็ณป็ตฑใฎ้ง…ใจๆ‰€่ฆๆ™‚้–“ใฎๆŽจๅฎšใŒๅฟ…่ฆใงใ€ใƒใ‚คใƒ†ใ‚ฃใƒ–็’ฐๅขƒใง็ด„ 190ms ใ‹ใ‹ใ‚Šใพใ™ (data/*.csv ใฎ 1,185 ็ณป็ตฑใƒป41,706 ่กŒใ€‚ๆ™‚้–“ใฎๅŠๅˆ†ใŒ Station ใฎ ็ต„ใฟ็ซ‹ใฆใ€ๆฎ‹ใ‚ŠๅŠๅˆ†ใŒๆŽจๅฎš)ใ€‚ใใฎใŸใ‚่ตทๅ‹•ๆ™‚ใซใฏๆง‹็ฏ‰ใ›ใšใ€ๆœ€ๅˆใซ connectedRoutes ใŒๅ‘ผใฐใ‚ŒใŸใจใใซ OnceLock ใซๆง‹็ฏ‰ใ—ใ€isolate ใŒ็”Ÿใใฆใ„ใ‚‹ ้–“ใฏไฝฟใ„ๅ›žใ—ใพใ™ใ€‚

ๆŽข็ดข (RAPTOR)

ๆ™‚ๅˆป่กจใฎใƒ‡ใƒผใ‚ฟใ‚’ๆŒใŸใชใ„ใŸใ‚ใ€้‹่กŒ้ ปๅบฆใซๅŸบใฅใ RAPTOR ใงๆŽข็ดขใ—ใพใ™ใ€‚ ใƒฉใ‚ฆใƒณใƒ‰ k ใงใฏใ€Œk ๆœฌ็›ฎใฎๅˆ—่ปŠใซไน—ใฃใŸๆ™‚็‚นใ€ใงใฎๆœ€่‰ฏๅ€คใ‚’ๆฑ‚ใ‚ใ€ๅ‰ใฎใƒฉใ‚ฆใƒณใƒ‰ใง ๅ€คใŒๆ”นๅ–„ใ—ใŸ้ง…ใ‚’้€šใ‚‹ใƒ‘ใ‚ฟใƒผใƒณใ ใ‘ใ‚’่ตฐๆŸปใ—ใพใ™ใ€‚ๅ„ใƒฉใ‚ฆใƒณใƒ‰ใงใฎ็›ฎ็š„ๅœฐใฎๅ€คใŒใ€ ใใฎใพใพใ€Œk ๆœฌไปฅๅ†…ใฎไน—่ปŠใงๅˆฐ้”ใงใใ‚‹ๆœ€่‰ฏๅ€คใ€ใซใชใ‚‹ใฎใงใ€ๆ‰€่ฆๆ™‚้–“ใจไน—ๆ›ๅ›žๆ•ฐใฎ ใƒ‘ใƒฌใƒผใƒˆ่งฃใŒๅพ—ใ‚‰ใ‚Œใพใ™ใ€‚

่ฉ•ไพกๅ€ค (็ง’) ใฏๆฌกใฎๅˆ่จˆใงใ™ใ€‚

้ …็›ฎ ๅ€ค
ไน—่ปŠๆ™‚้–“ arrival_estimation ใฎๆŽจๅฎšๅ€ค (ๅœ่ปŠๆ™‚้–“ใƒป้€š้Žใ‚’ๅซใ‚€)
ไน—่ปŠใ”ใจใฎๅพ…ใกๆ™‚้–“ ็‰นๆ€ฅใƒปๆ–ฐๅนน็ทš 15 ๅˆ† / ๆ€ฅ่กŒใƒปๆ–ฐๅฟซ้€Ÿใ‚ฏใƒฉใ‚น 5 ๅˆ† / ใใฎไป– 3 ๅˆ†
ไน—ๆ›ใฎๅพ’ๆญฉๆ™‚้–“ 3 ๅˆ†

ๅพ…ใกๆ™‚้–“ใ‚’ๅŠ ใˆใชใ„ใจใ€ๆœฌๆ•ฐใฎๅฐ‘ใชใ„็‰นๆ€ฅใŒใ€Œ็›ด้€šใง้€Ÿใ„ใ€ใจ่ฉ•ไพกใ•ใ‚Œใ€ ๆฑไบฌ โ†’ ๆธ‹่ฐทใงๅฑฑๆ‰‹็ทšใ‚ˆใ‚Šๆˆ็”ฐใ‚จใ‚ฏใ‚นใƒ—ใƒฌใ‚นใ‚’ๅ‹งใ‚ใฆใ—ใพใ„ใพใ™ใ€‚

ไปฃๆ›ฟ็ตŒ่ทฏใจไธฆใณ้ †

ใƒ‘ใƒฌใƒผใƒˆ่งฃใซใฏใ€ๆœ€้€Ÿใฎ็ตŒ่ทฏใจไน—ๆ›ใŒๆœ€ใ‚‚ๅฐ‘ใชใ„็ตŒ่ทฏใ—ใ‹ๅซใพใ‚Œใพใ›ใ‚“ใ€‚ใใ“ใงใ€ ่ฆ‹ใคใ‹ใฃใŸ็ตŒ่ทฏใฎๅŒบ้–“ใ‚’ 1 ใคใšใค็ฆๆญขใ—ใฆๅ†ๆŽข็ดขใ—ใพใ™ (Yen ใฎ k ๆœ€็Ÿญ็ตŒ่ทฏ ใ‚ขใƒซใ‚ดใƒชใ‚บใƒ ใฎ็ฐก็•ฅ็‰ˆใ€‚ๆŽข็ดขใฏๅˆๅ›žใ‚’ๅซใ‚ใฆๆœ€ๅคง 8 ๅ›ž)ใ€‚็ฆๆญขใ™ใ‚‹ใฎใฏใใฎๅŒบ้–“ใฎไธฆ่กŒ็ณป็ตฑ (ๅŒใ˜ ไน—่ปŠ้ง…ใจ้™่ปŠ้ง…ใซๅœ่ปŠใ™ใ‚‹ๅฟซ้€Ÿใƒปๅ„ๅœใชใฉ) ใงใ€ๅŒบ้–“ๅ†…ใฎใฉใฎ้ง…ใ‹ใ‚‰ใ‚‚ไน—่ปŠใงใ ใชใ„ใ‚ˆใ†ใซใ—ใพใ™ใ€‚ไน—่ปŠ้ง…ใ ใ‘ใ‚’็ฆๆญขใ™ใ‚‹ใจใ€ๅˆฅใฎๅˆ—่ปŠใง 1 ้ง…้€ฒใ‚“ใงใ‹ใ‚‰ๅŒใ˜ ๆ–ฐๅนน็ทšใซไน—ใ‚‹็ตŒ่ทฏใŒๅ‡บใฆใใฆใ—ใพใ†ใŸใ‚ใงใ™ใ€‚

้ †ไฝใฏใ€Œ่ฉ•ไพกๅ€ค + ไน—ๆ› 1 ๅ›žใซใคใ 5 ๅˆ†ใ€ใงๆฑบใ‚ใ€ๆœ€ๅคง 6 ไปถใ‚’่ฟ”ใ—ใพใ™ใ€‚ไปฃๆ›ฟ ็ตŒ่ทฏใฎใ†ใกใ€ใ“ใฎ้ †ไฝใฎๅ€คใŒๆœ€่‰ฏใฎ็ตŒ่ทฏใฎ 1.15 ๅ€ + 15 ๅˆ†ใ‚’่ถ…ใˆใ‚‹ใ‚‚ใฎใจใ€ ไน—ๆ›ๅ›žๆ•ฐใŒใƒ‘ใƒฌใƒผใƒˆ่งฃใฎๆœ€ๅคšไน—ๆ›ๅ›žๆ•ฐใ‚ˆใ‚Š 2 ๅ›žไปฅไธŠๅคšใ„ใ‚‚ใฎใฏๆจใฆใพใ™ใ€‚ไน—่ปŠ ๆœฌๆ•ฐใ‚’ๅข—ใ‚„ใ—ใฆใ‚‚้ †ไฝใฎๅ€คใŒ่‰ฏใใชใ‚‰ใชใ„ใƒ‘ใƒฌใƒผใƒˆ่งฃ (1 ๅˆ†็ธฎใ‚ใ‚‹ใŸใ‚ใซไน—ๆ›ใ‚’ ้‡ใญใ‚‹็ตŒ่ทฏ) ใ‚‚ๆจใฆใพใ™ใ€‚

ไปฃๆ›ฟ็ตŒ่ทฏใฎใ†ใกใ€ๅˆฅใ€…ใฎๅŒบ้–“ใงๅŒใ˜้ง…ใ‚ฐใƒซใƒผใƒ—ใซๅœ่ปŠใ™ใ‚‹ใ‚‚ใฎใ‚‚ๆจใฆใพใ™ใ€‚ๅŒบ้–“ใ‚’ ็ฆๆญขใ—ใฆๅ†ๆŽข็ดขใ™ใ‚‹ใจใ€ใ€Œ1 ้ง…ๆˆปใฃใฆๅŒใ˜ๅˆ—่ปŠใซไน—ใ‚Š็›ดใ™ใ€้€†ๆˆปใ‚Šใฎ็ตŒ่ทฏใŒไปฃๆ›ฟ ็ตŒ่ทฏใจใ—ใฆๅ‡บใฆใใ‚‹ใŸใ‚ใงใ™ (ไพ‹: ๅคงๅฎฎใ‹ใ‚‰ๅœŸๅ‘‚ใธ่กŒใใ€ๅคงๅฎฎใซๅœ่ปŠใ™ใ‚‹ๅˆ—่ปŠใง ๆฑไบฌใธๅ‘ใ‹ใ†)ใ€‚้€š้Žใ—ใŸ้ง…ใฏๆ•ฐใˆใพใ›ใ‚“ (ๆ€ฅ่กŒใง้€š้Žใ—ใŸ้ง…ใซใ€ๅ…ˆใฎ้ง…ใ‹ใ‚‰ ๆˆปใ‚‹ใฎใฏๅฎŸ้š›ใซใ‚ใ‚‹ไน—ใ‚Šๆ–นใงใ™)ใ€‚1 ใคใฎๅŒบ้–“ใฎไธญใงๅŒใ˜้ง…ใซ 2 ๅ›žๅœ่ปŠใ™ใ‚‹ใฎใฏ ๅฎŸๅœจใ™ใ‚‹้‹่กŒ (ๅคงๆฑŸๆˆธ็ทšใฎ้ƒฝๅบๅ‰ใชใฉ) ใชใฎใงใ€้™คๅค–ใ—ใพใ›ใ‚“ใ€‚ๅˆๅ›žๆŽข็ดขใฎ ใƒ‘ใƒฌใƒผใƒˆ่งฃ (ๆœ€้ฉ่งฃ) ใซใฏใ“ใฎ้™คๅค–ใ‚’้ฉ็”จใ—ใพใ›ใ‚“ใ€‚้ฉ็”จใ™ใ‚‹ใจใ€้€†ๆˆปใ‚Šใงใ—ใ‹ ๅˆฐ้”ใงใใชใ„้ง…ใŒใ€stationsByName ใงใฏใ€Œ่กŒใ‘ใ‚‹้ง…ใ€ใจใ—ใฆ่ฟ”ใ‚‹ใฎใซใ€็ตŒ่ทฏใŒ 0 ไปถใซใชใฃใฆใ—ใพใ†ใŸใ‚ใงใ™ใ€‚

ๅˆฐ็€่ฆ‹่พผใฟใจ่ตฐ่กŒๅŒบ้–“ (estimateArrivalTimes / trainRoute)

ใฉใกใ‚‰ใ‚‚ legs: [RouteLegInput!] ใ‚’ๅ—ใ‘ๅ–ใ‚Šใ€ไน—ๆ›็ตŒ่ทฏๅ…จไฝ“ใ‚’้€šใ—ใŸๅ€คใ‚’ ่ฟ”ใ—ใพใ™ใ€‚legs ใซใฏใ€connectedRoutes ใฎๅ„ๅŒบ้–“ใฎ trainTypes ใ‹ใ‚‰้ธใ‚“ใ  ็จฎๅˆฅใฎ groupId ใจใ€ใใฎๅŒบ้–“ใฎ fromStation.idใƒปtoStation.id ใ‚’ๆธกใ—ใพใ™ใ€‚ ็ตŒ่ทฏ ID ใฏๅญ˜ๅœจใ—ใชใ„ใฎใงใ€ใ‚ฏใƒฉใ‚คใ‚ขใƒณใƒˆใŒ็ตŒ่ทฏใ‚’ๅŒบ้–“ใฎไธฆใณใจใ—ใฆๆธกใ—ใพใ™ใ€‚

้ธใ‚“ใ ็จฎๅˆฅใŒใ€ๅŒบ้–“ใฎไน—่ปŠ้ง…ใƒป้™่ปŠ้ง…ใจใฏๅˆฅใฎ่ทฏ็ทšใฎ้ง…ใซๅœ่ปŠใ™ใ‚‹ใ“ใจใŒ ใ‚ใ‚Šใพใ™ (ใŸใจใˆใฐไน—้™้ง…ใฏไธญๅคฎ็ทšๅฟซ้€Ÿใฎไธ‰้ทนใ ใŒใ€้ธใ‚“ใ ๅ„้ง…ๅœ่ปŠใฏไธญๅคฎใƒป ็ทๆญฆ็ทšใฎไธ‰้ทนใซๅœ่ปŠใ™ใ‚‹ใ€ใชใฉ)ใ€‚ใใฎใŸใ‚ใ€็ณป็ตฑใซๅซใพใ‚Œใชใ„ไน—้™้ง…ใฏใ€ๅŒใ˜ ้ง…ใ‚ฐใƒซใƒผใƒ—ใฎ้ง…ใงไปฃ็”จใ—ใพใ™ใ€‚station_cd ใŒไธ€่‡ดใ™ใ‚‹้ง…ใŒใ‚ใ‚Œใฐใใ‚Œใ‚’ไฝฟใ„ใ€ ๅŒใ˜้ง…ใ‚ฐใƒซใƒผใƒ—ใฎๅ€™่ฃœใŒ่ค‡ๆ•ฐใ‚ใ‚‹ๅ ดๅˆใฏใ€ๅŒบ้–“ใŒๆœ€ใ‚‚็Ÿญใใชใ‚‹็ต„ใฟๅˆใ‚ใ›ใ‚’้ธใณ ใพใ™ใ€‚็›ด้€š็ณป็ตฑใฏใ€ๆŽฅ็ถš้ง…ใซๅŒใ˜้ง…ใ‚ฐใƒซใƒผใƒ—ใฎ้ง…ใ‚’ 2 ่กŒๆŒใคใŸใ‚ใงใ™ (ไพ‹: ๅฎ‡้ƒฝๅฎฎ็ทšใฎไธŠ้‡ŽใจไธŠ้‡Žๆฑไบฌใƒฉใ‚คใƒณใฎไธŠ้‡Ž)ใ€‚

input RouteLegInput { lineGroupId: Int!  fromStationId: Int!  toStationId: Int! }
  • estimateArrivalTimes: ๅ„ๅŒบ้–“ใ‚’ๆŒ‡ๅฎšใ•ใ‚ŒใŸ็ณป็ตฑใ ใ‘ใงๆŽจๅฎšใ— (ไธก้ง…ใซๅœ่ปŠ ใ™ใ‚‹ๅˆฅใฎ็ณป็ตฑใฏไฝฟใ„ใพใ›ใ‚“)ใ€ๅ‡บ็™บ้ง…ใ‹ใ‚‰ใฎ็ดฏ็ฉๆ™‚้–“ใงใคใชใ„ใ  1 ๆœฌใฎ็ตŒ่ทฏใ‚’ ่ฟ”ใ—ใพใ™ (็ณป็ตฑใ‚’ใพใŸใใฎใง id ใฏ็ฉบใงใ™)ใ€‚ไน—ๆ›้ง…ใฏใ€ๅ‰ใฎๅŒบ้–“ใฎ้™่ปŠ้ง…ใจ ๆฌกใฎๅŒบ้–“ใฎไน—่ปŠ้ง…ใฎ 2 ่กŒใซใชใ‚Šใพใ™ใ€‚ไน—่ปŠ้ง…ใฎ่กŒใซใฏใ€ใ€Œๅพ’ๆญฉ 3 ๅˆ†ๅพŒใซๅˆฐ็€ใ—ใ€ ไน—ๆ›ๅ…ˆใฎ็จฎๅˆฅใฎๅพ…ใกๆ™‚้–“ใŒ็ตŒใฃใฆใ‹ใ‚‰ๅ‡บ็™บใ™ใ‚‹ใ€ๅ€คใŒๅ…ฅใ‚Šใพใ™ใ€‚ใ“ใฎ่ฆ‹่พผใฟใฏใ€ connectedRoutes ใฎไธฆในๆ›ฟใˆใซไฝฟใ†ใ‚‚ใฎ (ๅพ’ๆญฉ 3 ๅˆ†ใจ็จฎๅˆฅใ”ใจใฎๅพ…ใกๆ™‚้–“) ใจ ๅŒใ˜ใงใ™ใ€‚
  • trainRoute: ๅŒบ้–“ใ”ใจใฎ่ตฐ่กŒๅŒบ้–“ใ‚’้ †ใซใคใชใ’ใพใ™ใ€‚ๅŒบ้–“ใ”ใจใซๅˆฅใฎๅˆ—่ปŠ ใชใฎใงใ€ๅ„ๅŒบ้–“ใฎๆœ€ๅˆใฎ้ง…ใฎ distanceFromPrevious ใฏ 0 ใซใชใ‚Šใ€้€š้Ž้ง…ใŒ ใ‚ใ‚‹ใ‹ (ๅ„ช็ญ‰็จฎๅˆฅใฎ้€Ÿๅบฆใ‚’ไฝฟใ†ใ‹) ใ‚‚ๅŒบ้–“ใ”ใจใซๅˆคๅฎšใ—ใพใ™ใ€‚

trainRoute ใฏใ€ๅŒบ้–“ใฎๅ€คใ‚’ใฉใฎใƒขใƒ‡ใƒซใงๅ‡บใ™ใ‹ใ‚’ model: TrainRouteModel ใง ้ธในใพใ™ใ€‚

  • Legacy (็œ็•ฅๆ™‚): ่ฟฝๅŠ ใ—ใŸๆ™‚็‚น (#1568) ใฎใƒขใƒ‡ใƒซใงใ™ใ€‚ๆœ€้ซ˜้€ŸๅบฆใจๅŠ ๆธ›้€Ÿใฏ dto::simulation::resolve_speed_profile ใŒๆฑบใ‚ใ€ๅˆฐ็€ใƒปๅ‡บ็™บใฎ่ฆ‹่พผใฟ (arrivalCumulativeMinutes / departureCumulativeMinutes) ใฏ null ใงใ™ใ€‚ ้…ๅธƒๆธˆใฟใฎ MobileApp ใฎใ‚ชใƒผใƒˆใƒขใƒผใƒ‰ใŒใ“ใฎๅ€คใง่ตฐใ‚‹ใฎใงใ€ๅ€คใ‚’ๅค‰ใˆใพใ›ใ‚“ใ€‚้€Ÿๅบฆใฎ่ผƒๆญฃใฏใ€ ๅˆฐ็€ๆ™‚้–“ๆŽจๅฎšใฎ่ผƒๆญฃใ‚’ๆฑ‚ใ‚็›ดใ™ๅ‰ใฎ่กจใ‚’ domain/legacy_speed_table.rs ใซๅ‡็ตใ—ใฆ ไฝฟใ„ใพใ™ใ€‚
  • Estimated: ๅˆฐ็€ๆ™‚้–“ๆŽจๅฎš (arrival_estimation) ใฎใƒขใƒ‡ใƒซใงใ€MobileApp ใฎ ใ‚ชใƒผใƒˆใƒขใƒผใƒ‰ใจ GPX ใฎ็”ŸๆˆใŒไฝฟใ„ใพใ™ใ€‚่ฟ”ใ™้ง…ๅˆ—ใซๆŽจๅฎšใ‚’ๆŽ›ใ‘ใ€ๅœ่ปŠใƒป้€š้Žใ€ๆœ€้ซ˜้€Ÿๅบฆใ€ ๅŠ ๆธ›้€Ÿใ‚’ๆŽจๅฎšใŒไฝฟใฃใŸๅ€คใซ็ฝฎใๆ›ใˆใฆใ€ๅˆฐ็€ใƒปๅ‡บ็™บใฎ่ฆ‹่พผใฟใ‚’ๅ…ฅใ‚Œใพใ™ใ€‚่ฆ‹่พผใฟใฏใ€ ๅŒใ˜ๅŒบ้–“ใฎ estimateArrivalTimes ใจๅŒใ˜ๅ€คใงใ™ (legs ใ‚’ๆธกใ—ใŸใจใใฏใ€ๅŒใ˜ legs ใ‚’ๆธกใ—ใŸ estimateArrivalTimes ใจๅŒใ˜ๅ€ค)ใ€‚ใƒใ‚นใฎ้ง…ใ‚’ๅซใ‚€็ตŒ่ทฏใฏๆŽจๅฎšใฎ ใƒขใƒ‡ใƒซใฎๅฏพ่ฑกๅค–ใชใฎใงใ€Legacy ใจๅŒใ˜ๅ€คใ‚’่ฟ”ใ—ใพใ™ใ€‚

2 ใคใฎใƒขใƒ‡ใƒซใฏใ€ๅŠ ๆธ›้€Ÿใ€้‹่ปขไฝ™่ฃ•็އใ€ๅœ่ปŠๆ™‚้–“ใ€่ผƒๆญฃใƒ†ใƒผใƒ–ใƒซใ€้ง…้–“ใฎ่ท้›ขใŒ้•ใ„ ใพใ™ใ€‚Estimated ใฏใ€็ทš่ทฏใฎ้•ทใ• (connections) ใŒใ‚ใ‚‹้ง…้–“ใงใฏใใ‚Œใ‚’่ตฐ่กŒ่ท้›ขใซ ไฝฟใ„ใ€็„กใ„้ง…้–“ใ ใ‘็›ด็ทš่ท้›ข ร— ่ฟ‚ๅ›žไฟ‚ๆ•ฐใง่ฆ‹็ฉใ‚‚ใ‚Šใ€ใใฎ่ท้›ขใงๆฑ‚ใ‚็›ดใ—ใŸ่ผƒๆญฃ (speed_table / segment_speed_table) ใ‚’ไฝฟใ„ใพใ™ใ€‚estimateArrivalTimes ใ‚‚ๅŒใ˜ ่จˆ็ฎ—ใงใ™ใ€‚ไน—ๆ›็ตŒ่ทฏๆŽข็ดข (connectedRoutes) ใฎๆ‰€่ฆๆ™‚้–“ใ ใ‘ใฏใ€ๅ…ƒใฎ่จˆ็ฎ— (็›ด็ทš่ท้›ข ร— ่ฟ‚ๅ›žไฟ‚ๆ•ฐใจใ€domain/legacy_speed_table.rs ใฎๅ…ƒใฎ่ผƒๆญฃ) ใฎใพใพใงใ™ใ€‚ใใฎใŸใ‚ใ€ ็ตŒ่ทฏๆคœ็ดขใฎๆ‰€่ฆๆ™‚้–“ใจ ETA ใฏไธ€่‡ดใ—ใพใ›ใ‚“ใ€‚Legacy ใฎๅ€คใงๅฐๅฝขใฎ้€Ÿๅบฆใƒ—ใƒญใƒ•ใ‚กใ‚คใƒซใ‚’ไฝœใฃใฆ่ตฐใ‚‰ใ›ใ‚‹ใจใ€ estimateArrivalTimes ใ‚ˆใ‚Š็Ÿญใ„ๆ™‚้–“ใง่ตฐใ‚Šๅˆ‡ใ‚Šใพใ™ (#1709)ใ€‚ๆ‰€่ฆๆ™‚้–“ใ‚’ๆŽจๅฎšใซ ๅˆใ‚ใ›ใŸใ„ใ‚ฏใƒฉใ‚คใ‚ขใƒณใƒˆใฏใ€Estimated ใฎ่ฆ‹่พผใฟใ‚’ไฝฟใ„ใพใ™ใ€‚

ๅŒบ้–“ใฎๅˆ‡ใ‚Šๅ‡บใ—ใซใฏไธก่€…ใงๅŒใ˜้–ขๆ•ฐใ‚’ไฝฟใ„ใ€็’ฐ็Šถ็ทšใงใฏ็ถ™ใŽ็›ฎใ‚’ใพใŸใ็Ÿญใ„ใปใ†ใฎ ๅผงใ‚’้ธใถใฎใงใ€ไธก่€…ใฎ้ง…ใฎไธฆใณใฏไธ€่‡ดใ—ใพใ™ (lineGroupId ใ‚’ๆŒ‡ๅฎšใ—ใŸ trainRoute ใฏใ€ๅพ“ๆฅใฉใŠใ‚Šๆ ผ็ด้ †ใงๅˆ‡ใ‚Šๅ‡บใ—ใพใ™)ใ€‚ๆฌกใฎใ„ใšใ‚Œใ‹ใซๅฝ“ใฆใฏใพใ‚‹ ๅ ดๅˆใฏใ‚จใƒฉใƒผใซใชใ‚Šใพใ™ใ€‚

  • ๅŒบ้–“ใŒใคใชใŒใฃใฆใ„ใชใ„ (ๅ‰ใฎๅŒบ้–“ใฎ้™่ปŠ้ง…ใจๆฌกใฎๅŒบ้–“ใฎไน—่ปŠ้ง…ใŒใ€ๅˆฅใฎ ้ง…ใ‚ฐใƒซใƒผใƒ—ใซใ‚ใ‚‹)
  • ๅŒบ้–“ใฎๆ•ฐใŒ 6 (MAX_RIDESใ€‚connectedRoutes ใŒ่ฟ”ใ—ใ†ใ‚‹ๆœ€ๅคงใฎไน—่ปŠๅ›žๆ•ฐ) ใ‚’ ่ถ…ใˆใ‚‹
  • ไธก็ซฏใฎ้ง…ใŒ fromStationId / toStationId ใจไธ€่‡ดใ—ใชใ„
  • viaLineIdsใƒปdirectionIdใƒปlineGroupId ใจๅŒๆ™‚ใซๆŒ‡ๅฎšใ•ใ‚Œใฆใ„ใ‚‹

ๆ‰€่ฆๆ™‚้–“ใฎใƒ™ใƒณใƒใƒžใƒผใ‚ฏ (travel_times/)

ๅˆฐ็€ๆ™‚้–“ๆŽจๅฎš (estimateArrivalTimes ใจ trainRoute ใฎ Estimated) ใฎๆ‰€่ฆๆ™‚้–“ใ‚’ใ€ๅฎŸ้š›ใฎๅˆ—่ปŠใฎๆ‰€่ฆๆ™‚้–“ใจๆฏ”ในใ‚‹ๅŸบๆบ–ใ‚’ travel_times/cases.csv ใซ็ฝฎใ„ใฆใ„ใพใ™ใ€‚้€Ÿๅบฆใฎ่ผƒๆญฃใƒ†ใƒผใƒ–ใƒซใ‚„ไธ€่ˆฌๅ‰‡ใฏใ€1 ใคใฎ่ทฏ็ทšใซๅˆใ‚ใ›ใฆๅค‰ใˆใ‚‹ใจใ€ๅŒใ˜ ่ฆๅ‰‡ใ‚’ไฝฟใ†ใปใ‹ใฎ่ทฏ็ทšใฎๆŽจๅฎšใ‚‚ๅค‰ใ‚ใ‚Šใพใ™ใ€‚ๅค‰ๆ›ดใฎๅ‰ๅพŒใงๅ…จไฝ“ใฎ่ชคๅทฎใ‚’ๆธฌใ‚‹ใŸใ‚ใฎไป•็ต„ใฟ ใงใ™ใ€‚

  • cargo test -p stationapi-worker (src/travel_times.rs) ใฏใ€ๅŸบๆบ–ใ”ใจใฎใ€ŒๅฎŸ้š›ใฎ ๅ…ธๅž‹็š„ใชๆ‰€่ฆๆ™‚้–“ใ‹ใ‚‰ใฎใšใ‚Œใ€ใ‚’ travel_times/baseline.csv ใฎ่จ˜้Œฒใจๆฏ”ในใ€ๆ‚ชใใชใ‚‹ ใจๅคฑๆ•—ใ—ใพใ™ใ€‚่จ˜้Œฒใฏๆœฌ็•ชใจๅŒใ˜็”Ÿๆˆใƒ‡ใƒผใ‚ฟใงไฝœใ‚‹ใฎใงใ€ๆฏ”ในใ‚‹ใฎใฏ generated/ ใง ๅ‹•ใใจใใ ใ‘ใงใ™ใ€‚ CI ใงใฏ build_worker.yml ใŒ็”Ÿๆˆใƒ‡ใƒผใ‚ฟใ‚’ไฝœใฃใฆใ‹ใ‚‰่ตฐใ‚‰ใ›ใพใ™ใ€‚
  • make travel-time-report ใฏใ€็”Ÿๆˆใƒ‡ใƒผใ‚ฟใงๅ‹•ใ Worker ใซๅ•ใ„ๅˆใ‚ใ›ใฆๅ…จไปถใฎ่ชคๅทฎใ‚’ ๅ‡บใ—ใพใ™ใ€‚ๆŽจๅฎšใฎ่ฆๅ‰‡ใ‚„่ผƒๆญฃใ‚’ๅค‰ใˆใ‚‹ PR ใซใฏใ€ๅค‰ๆ›ดๅ‰ใจๅค‰ๆ›ดๅพŒใฎใƒฌใƒใƒผใƒˆใ‚’่ผ‰ใ›ใพใ™ใ€‚

ๅŸบๆบ–ใฎๆฑบใ‚ๆ–นใจ่จ˜้Œฒใฎๆ›ดๆ–ฐๆ–นๆณ•ใฏ travel_times/README.md ใซใ‚ใ‚Šใพใ™ใ€‚

่กŒใๅ…ˆใฎๆคœ็ดข (stationsByName)

stationsByName ใซ fromStationGroupId ใ‚’ๆŒ‡ๅฎšใ™ใ‚‹ใจใ€ใใฎ้ง…ใ‹ใ‚‰่กŒใ‘ใ‚‹้ง…ใ ใ‘ใซ ็ตžใ‚Š่พผใฟใพใ™ใ€‚่ฟ”ใ™ใฎใฏๆฌกใฎ้ง…ใงใ™ใ€‚

  • ๅ‡บ็™บ้ง…ใจ็ณป็ตฑใ‚’ๅ…ฑๆœ‰ใ™ใ‚‹้ง… (็›ด้€šใง่กŒใ‘ใ‚‹้ง…)
  • ใฉใกใ‚‰ใ‹ใŒ็ณป็ตฑใ‚’ๆŒใŸใชใ„ๅ ดๅˆใฏใ€ๅŒใ˜่ทฏ็ทšใฎ้ง…
  • ไน—ใ‚Šๆ›ใˆใ‚Œใฐใ€ใใฎ้ง…ใฎ่ทฏ็ทšใฎๅˆ—่ปŠใงๅˆฐ็€ใงใใ‚‹้‰„้“้ง…

3 ใค็›ฎใฏใ€ใ€ŒviaLineId ใซใใฎ้ง…ใฎ่ทฏ็ทšใ‚’ๆŒ‡ๅฎšใ—ใฆ connectedRoutes ใ‚’ๅ‘ผใถใจ ็ตŒ่ทฏใŒ่ฟ”ใ‚‹้ง…ใ€ใจไธ€่‡ดใ•ใ›ใฆใ„ใพใ™ใ€‚ใ“ใ‚Œใ‚‰ใฎ้ง…ใฏๅ‡บ็™บ้ง…ใจ็ณป็ตฑใ‚’ๅ…ฑๆœ‰ใ—ใชใ„ใฎใง line_group_cd ใฏ็ฉบใ€hasTrainTypes ใฏๅฝใซใชใ‚Šใ€็›ด้€šใง่กŒใ‘ใ‚‹้ง…ใจๅŒบๅˆฅ ใงใใพใ™ใ€‚

ๅˆคๅฎšใซใฏใ€connectedRoutes ใจๅŒใ˜็ณป็ตฑใ‹ใ‚‰ไฝœใฃใŸๆ‰€่ฆๆ™‚้–“ใ‚’ๆŒใŸใชใ„็ถฒ (stationapi/src/domain/route_topology.rs ใฎ RouteTopology) ใ‚’ไฝฟใ„ใพใ™ใ€‚ ๅฟ…่ฆใชใฎใฏใ€Œใฉใฎ็ณป็ตฑใŒใฉใฎ้ง…ใซๅœ่ปŠใ™ใ‚‹ใ‹ใ€ใ ใ‘ใชใฎใงใ€Station ใฎ็ต„ใฟ็ซ‹ใฆใ‚‚ ๆ‰€่ฆๆ™‚้–“ใฎๆŽจๅฎšใ‚‚ใ›ใšใซ็ดขๅผ•ใ‹ใ‚‰็›ดๆŽฅๆง‹็ฏ‰ใงใใ€ๆง‹็ฏ‰ๆ™‚้–“ใฏ็ด„ 20ms (ๆ‰€่ฆๆ™‚้–“ ใคใใฎ็ถฒใฎ็ด„ 1/10) ใงใ™ใ€‚RouteNetwork ใ‚‚ๅ†…้ƒจใซๅŒใ˜็ถฒใ‚’ๆŒใฃใฆใŠใ‚Šใ€็ณป็ตฑใฎ ๆ•ดๅฝข (trim_pattern) ใจ้ง…ใฎ้ธใณๆ–น (line_group_rows) ใ‚’ๅ…ฑๆœ‰ใ—ใฆใ„ใพใ™ใ€‚ ไธก่€…ใŒไธ€่‡ดใ™ใ‚‹ใ“ใจใฏใ€ๅฎŸใƒ‡ใƒผใ‚ฟใ‚’ไฝฟใฃใŸใƒ†ใ‚นใƒˆใง็ขบ่ชใ—ใฆใ„ใพใ™ใ€‚

ๅˆคๅฎšใงใฏใ€ไน—่ปŠ 6 ๆœฌไปฅๅ†…ใฎ็ฏ„ๅ›ฒใง็ณป็ตฑใ‚’ๅน…ๅ„ชๅ…ˆๆŽข็ดขใ—ใพใ™ (1 ๅ›žใ‚ใŸใ‚Šๆ•ฐ ms)ใ€‚ ใŸใ ใ— connectedRoutes ใฎๆŽข็ดขใฏใ€็›ฎ็š„ๅœฐใฎ้ง…ใ‚ฐใƒซใƒผใƒ—ใง้€”ไธญไธ‹่ปŠใ—ใฆไน—ใ‚Š็ถ™ใ ็ตŒ่ทฏใ‚’ไฝœใ‚Šใพใ›ใ‚“ใ€‚ใใฎใŸใ‚ๅ˜็ด”ใชๅน…ๅ„ชๅ…ˆๆŽข็ดขใงใฏใ€ใ€Œๆ”ฏ็ทšใฎๅˆ†ๅฒ้ง…ใซใ€ใ„ใฃใŸใ‚“ ๆ”ฏ็ทšใธๅ‡บใฆใ‹ใ‚‰ๆˆปใฃใฆใใฆๅˆฐ็€ใ™ใ‚‹ใ€็ตŒ่ทฏใพใงๆ•ฐใˆใฆใ—ใพใ„ใพใ™ (็ฎ•้ข็ทšใง ็Ÿณๆฉ‹้˜ชๅคงๅ‰ใซ็€ใใ€ๅ‡ฝ้คจๆœฌ็ทšใงๆ–ฐๅ‡ฝ้คจๅŒ—ๆ–—ใซ็€ใใ€ใชใฉใ€‚ๅฎŸใƒ‡ใƒผใ‚ฟใงใฏ 0.3ใ€œ0.7% ใฎ้ง…ใŒ่ฉฒๅฝ“ใ—ใพใ™)ใ€‚ใ“ใ‚ŒใŒ่ตทใใ‚‹ใฎใฏใ€็›ฎ็š„ๅœฐใŒใ€Œ้ง…ใจ็ณป็ตฑใ‹ใ‚‰ใชใ‚‹ไบŒ้ƒจใ‚ฐใƒฉใƒ•ใ€ ใฎ้–ข็ฏ€็‚นใงใ‚ใ‚‹ๅ ดๅˆใซ้™ใ‚‰ใ‚Œใพใ™ใ€‚ใใ“ใง็ถฒใฎๆง‹็ฏ‰ๆ™‚ใซ้–ข็ฏ€็‚นใ‚’ๆฑ‚ใ‚ใฆใŠใ (Tarjan ใฎใ‚ขใƒซใ‚ดใƒชใ‚บใƒ )ใ€่ฉฒๅฝ“ใ™ใ‚‹้ง…ใซใคใ„ใฆใ ใ‘ใ€็›ฎ็š„ๅœฐใง้€”ไธญไธ‹่ปŠใ—ใชใ„ ๅน…ๅ„ชๅ…ˆๆŽข็ดขใงๆ”นใ‚ใฆ็ขบ่ชใ—ใพใ™ใ€‚ๅฐŽๅ…ฅๆ™‚ใซใฏใ€ๅฎŸใƒ‡ใƒผใ‚ฟใฎ 3 ใคใฎๅ‡บ็™บ้ง…ใซใคใ„ใฆ ใใ‚Œใžใ‚Œ 1,500 ้ง…ใ‚’็ชใๅˆใ‚ใ›ใ€connectedRoutes ใฎๆŽข็ดข็ตๆžœใจ้ฃŸใ„้•ใ„ใŒ ใชใ„ใ“ใจใ‚’็ขบ่ชใ—ใพใ—ใŸใ€‚็ถฒใฎๆง‹็ฏ‰ๅพŒใงใ‚ใ‚Œใฐใ€stationsByName ใฏ 100 ไปถใƒ’ใƒƒใƒˆใ™ใ‚‹ ๅ ดๅˆใงใ‚‚ 2ใ€œ4ms ใงๅฟœ็ญ”ใ—ใพใ™ใ€‚

่จˆ็ฎ—้‡

1 ๅ›žใฎๆŽข็ดขใฎ่จˆ็ฎ—้‡ใฏ O(ไน—่ปŠๅ›žๆ•ฐ ร— ่ตฐๆŸปใ—ใŸใƒ‘ใ‚ฟใƒผใƒณใฎ้ง…ๆ•ฐ) ใงใ™ใ€‚ๆ—งๅฎŸ่ฃ… (ๅˆ—่ปŠ็จฎๅˆฅใจ้™่ปŠ้ง…ใฎใ™ในใฆใฎ็ต„ใฟๅˆใ‚ใ›ใ‚’ใ€ไน—ๆ›ๅ›žๆ•ฐใฎๅˆ†ใ ใ‘ๅˆ—ๆŒ™ใ™ใ‚‹ๆทฑใ•ๅˆถ้™ ใคใใฎ BFS) ใจใฎๆฏ”่ผƒใฏๆฌกใฎใจใŠใ‚Šใงใ™ (ใƒใ‚คใƒ†ใ‚ฃใƒ–ใฎ release ใƒ“ใƒซใƒ‰ใ€ data/*.csv ใ‚’ไฝฟ็”จ)ใ€‚

ๅŒบ้–“ ๆ—งๅฎŸ่ฃ… ๆ–ฐๅฎŸ่ฃ… (ๆŽข็ดขใฎใฟ)
ๆฑไบฌ โ†’ ๆธ‹่ฐท 370ms 11ใ€œ13ms
ไธ‰้ทน โ†’ ไธญ็›ฎ้ป’ 173ms (0 ไปถ) 13ใ€œ18ms
ๅคงๅฎฎ โ†’ ๆ–ฐๅคง้˜ช 349ms 31ใ€œ36ms
ไป™ๅฐ โ†’ ๅšๅคš 693ms 14ใ€œ20ms

ๆ™‚ๅˆป่กจใ€้‹่ปข้–“้š”ใ€้ง…ใ‚ฐใƒซใƒผใƒ—ใ‚’ใพใŸใๅพ’ๆญฉ้€ฃ็ตกใฎใƒ‡ใƒผใ‚ฟใŒใชใ„ใŸใ‚ใ€ๅพ…ใกๆ™‚้–“ใฏ ็จฎๅˆฅใ‹ใ‚‰่ฆ‹็ฉใ‚‚ใฃใŸๅ€คใงใ™ใ€‚ ๅญฃ็ฏ€้‹่กŒใฎ่‡จๆ™‚ๅˆ—่ปŠใ‚‚ใ€้€šๅธธใฎ็ณป็ตฑใจๅŒใ˜ใ‚ˆใ†ใซๆ‰ฑใ„ใพใ™ใ€‚


GraphQL ใจใ‚นใ‚ญใƒผใƒžไธ€่‡ดใฎๆ‹…ไฟ

ใ‚ฏใƒฉใ‚คใ‚ขใƒณใƒˆใจใฎไบ’ๆ›ๆ€งใ‚’ไฟใคใŸใ‚ใ€ใ‚จใƒณใƒ‰ใƒใ‚คใƒณใƒˆใฏใ‚ตใƒ–ใƒ‰ใƒกใ‚คใƒณใฎใƒซใƒผใƒˆใง ใ‚ฏใ‚จใƒชใ‚’ๅ—ใ‘ไป˜ใ‘ใพใ™ใ€‚

ใƒ‘ใ‚น ๅ†…ๅฎน
POST / ใ‚ฏใ‚จใƒชใฎๅฎŸ่กŒ
GET / GraphiQL
GET /__schema SDL (CI ใŒๅ–ๅพ—ใ—ใฆ็ชใๅˆใ‚ใ›ใ‚‹)
GET /__health ็ดขๅผ•ใฎไปถๆ•ฐ
GET /__ping ใƒ‡ใƒผใ‚ฟใซ่งฆใ‚Œใชใ„็–Ž้€š็ขบ่ช

async-graphql ใฏใ‚ณใƒผใƒ‰ใƒ•ใ‚กใƒผใ‚นใƒˆใชใฎใงใ€Rust ใฎๅž‹ใ‚’ๅค‰ๆ›ดใ™ใ‚‹ใจ SDL ใ‚‚ ๅค‰ใ‚ใ‚Šใพใ™ใ€‚ใ‚ฏใƒฉใ‚คใ‚ขใƒณใƒˆใ‚’ๅฃŠใ™ๅค‰ๆ›ดใซๆฐ—ไป˜ใ‘ใ‚‹ใ‚ˆใ†ใ€schema/public.graphql ใ‚’ ๆญฃใจใ—ใฆ scripts/compare_schema.py ใง็ชใๅˆใ‚ใ›ใ€ๅทฎๅˆ†ใŒใ‚ใ‚Œใฐ CI ใ‚’ๅคฑๆ•— ใ•ใ›ใพใ™ใ€‚ๅž‹ใจใƒ•ใ‚ฃใƒผใƒซใƒ‰ใฏ้›†ๅˆใจใ—ใฆๆฏ”่ผƒใ—ใ€enum ใฏ้ †ๅบใ‚‚ๅซใ‚ใฆๆฏ”่ผƒใ—ใพใ™ใ€‚

ใ‚นใ‚ญใƒผใƒžใ‚’ๆ„ๅ›ณ็š„ใซๅค‰ๆ›ดใ™ใ‚‹ใจใใฏใ€ใ“ใฎใƒ•ใ‚กใ‚คใƒซใ‚‚ๆ›ดๆ–ฐใ—ใพใ™ใ€‚ใใฎๅทฎๅˆ†ใŒใ€ ใใฎใพใพใ‚ฏใƒฉใ‚คใ‚ขใƒณใƒˆใธใฎๅฝฑ้Ÿฟ็ฏ„ๅ›ฒใซใชใ‚Šใพใ™ใ€‚

ๅฎŸ่ฃ…ไธŠใฎๆณจๆ„:

  • async-graphql ใฏๆ—ขๅฎšใง enum ใฎๅ€คใ‚’ SCREAMING_SNAKE_CASE ใซใ™ใ‚‹ใ€‚ๅ…ฌ้–‹ ใ‚นใ‚ญใƒผใƒžใฏ PascalCase ใชใฎใงใ€rename_items ใงๅˆใ‚ใ›ใฆใ„ใ‚‹
  • PascalCase ใซๅค‰ๆ›ใ™ใ‚‹ใจ JR ใŒ Jr ใซใชใ‚‹ใŸใ‚ใ€ใ“ใฎๅ€คใ ใ‘ใฏ name ใ‚’ ๆ˜Ž็คบใ—ใฆใ„ใ‚‹
  • Station ใจ StationNested ใฎใ‚ˆใ†ใซใ€ๆง‹้€ ใŒๅŒใ˜ใงๅๅ‰ใ ใ‘ใŒ้•ใ†ๅž‹ใฏใ€ SDL ใ‚’ๅˆใ‚ใ›ใ‚‹ใŸใ‚ใซใƒžใ‚ฏใƒญใงไธกๆ–นใ‚’ๅฎš็พฉใ—ใฆใ„ใ‚‹ใ€‚Nested ๅž‹ใฏไบ’ใ„ใซๅ‚็…ง ใ—ๅˆใ†ใฎใงใ€Box ใง้–“ๆŽฅๅ‚็…งใซใ—ใชใ„ใจ็„ก้™ใ‚ตใ‚คใ‚บใฎๅž‹ใซใชใ‚‹

ๅ‘ฝๅ่ฆๅ‰‡

ๅŒใ˜ใ€Œ้ง…ใ€ใ‚’่กจใ™ๅž‹ใŒใ€ๅฑคใ”ใจใซ 3 ใคใ‚ใ‚Šใพใ™ใ€‚

็จฎๅˆฅ ๅ ดๆ‰€ ็›ฎ็š„ ็‰นๅพด
Record src/index.rs ๅŸ‹ใ‚่พผใฟ CSV ใฎ 1 ่กŒ ๆคœ็ดขใซๅฟ…่ฆใชๅˆ—ใ ใ‘ใ‚’ๆŒใค่ปฝ้‡ใชๆง‹้€ ไฝ“
Entity stationapi/src/domain/entity/ ใƒ‰ใƒกใ‚คใƒณใƒขใƒ‡ใƒซ ใƒใ‚นใƒˆๆง‹้€ ใ€ๅคš่จ€่ชžๅฏพๅฟœใ€็ด„ 65 ใƒ•ใ‚ฃใƒผใƒซใƒ‰
Model stationapi/src/model.rs API ใŒ่ฟ”ใ™ๅ€ค ๅˆ—ๆŒ™ๅž‹ใ‚’ i32 ใฎใพใพไฟๆŒใ™ใ‚‹

Record ๆง‹้€ ไฝ“

// src/index.rs
pub struct StationRecord {
    pub station_cd: i32,
    pub station_g_cd: i32,
    pub name: String,
    // ๆคœ็ดขใซไฝฟใ†ๅˆ—ใ ใ‘ใ‚’ๆŒใคใ€‚ใƒฌใ‚นใƒใƒณใ‚น็”จใฎ Station ใฏๅฟ…่ฆใซใชใฃใŸๆ™‚็‚นใง็ต„ใฟ็ซ‹ใฆใ‚‹
}

ๅๅ‰ใซใ‚ˆใ‚‹ๆคœ็ดขใฏใƒชใ‚ฏใ‚จใ‚นใƒˆใฎใŸใณใซๅ…จไปถ่ตฐๆŸปใ™ใ‚‹ใฎใงใ€็ดขๅผ•ใซใฏ Station ใ‚จใƒณใƒ†ใ‚ฃใƒ†ใ‚ฃ (65 ใƒ•ใ‚ฃใƒผใƒซใƒ‰) ใ‚’ๆŒใŸใ›ใšใ€ใƒฌใ‚นใƒใƒณใ‚นใ‚’็”Ÿๆˆใ™ใ‚‹ใจใใซใ ใ‘ ็ต„ใฟ็ซ‹ใฆใพใ™ใ€‚ใƒญใƒผใƒžๅญ—ๅใฎๅฐๆ–‡ๅญ—็‰ˆใฎใ‚ˆใ†ใซใ€ๆฏ”่ผƒใฎใŸใณใซ่จˆ็ฎ—ใ™ใ‚‹ใจใ‚ณใ‚นใƒˆใŒ ใ‹ใ‹ใ‚‹ๅ€คใฏใ€็ดขๅผ•ใฎๆง‹็ฏ‰ๆ™‚ใซ่จˆ็ฎ—ใ—ใฆๆŒใฃใฆใŠใใพใ™ใ€‚

Entity ๆง‹้€ ไฝ“

// stationapi/src/domain/entity/station.rs
pub struct Station {
    pub station_cd: i32,
    pub line: Option<Box<Line>>,
    pub lines: Vec<Line>,
    pub station_numbers: Vec<StationNumber>,
    // ...
}
  • ใƒ“ใ‚ธใƒใ‚นไธŠใฎๆ„ๅ‘ณใ‚’ๅๆ˜ ใ—ใŸๅž‹ (StopCondition ๅˆ—ๆŒ™ๅž‹ใชใฉ) ใ‚’ไฝฟใ†
  • ๅคš่จ€่ชžใฎๅ็งฐ: station_name_r (ใƒญใƒผใƒžๅญ—)ใ€station_name_zhใ€station_name_ko

ๅค‰ๆ›ใƒ•ใƒญใƒผ

generated/*.csv
    โ†“  ่ตทๅ‹•ๆ™‚ใซไธ€ๅบฆใ ใ‘ใƒ‘ใƒผใ‚น
Record (StationRecord)
    โ†“  to_entity(): ่ทฏ็ทšใฎๅฑžๆ€งใ‚’ๅŸ‹ใ‚ใ‚‹
Entity (Station)
    โ†“  UseCase ๅฑคใงใƒใ‚นใƒˆใ—ใŸใƒ‡ใƒผใ‚ฟใ‚’ไป˜ไธŽ
Enriched Entity
    โ†“  DTO ๅค‰ๆ›: IPA / TTS ใ‚ปใ‚ฐใƒกใƒณใƒˆใ‚’็”Ÿๆˆ
Model (model::Station)
    โ†“  From ใซใ‚ˆใ‚‹ๅค‰ๆ›
GraphQL ๅž‹

ใƒ‡ใƒผใ‚ฟใƒ•ใƒญใƒผ

ๅ…ธๅž‹็š„ใชใƒชใ‚ฏใ‚จใ‚นใƒˆใฎๆตใ‚Œ

[Client]
    โ”‚
    โ–ผ POST / (GraphQL)
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Presentation (src/graphql/query.rs)           โ”‚
โ”‚  โ””โ”€ Query::station()                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
    โ”‚
    โ–ผ QueryUseCase ใฎใƒกใ‚ฝใƒƒใƒ‰ๅ‘ผใณๅ‡บใ—
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ UseCase (use_case/interactor/query.rs)        โ”‚
โ”‚  โ”œโ”€ QueryInteractor::find_station_by_id()     โ”‚
โ”‚  โ””โ”€ update_station_vec_with_attributes()      โ”‚
โ”‚      โ”œโ”€ ๅŒใ˜้ง…ใ‚ฐใƒซใƒผใƒ—ใฎ้ง…ใ‚’ไธ€ๆ‹ฌๅ–ๅพ—            โ”‚
โ”‚      โ”œโ”€ ่ทฏ็ทšใ‚’ไธ€ๆ‹ฌๅ–ๅพ—                          โ”‚
โ”‚      โ”œโ”€ ่ฟ‘ๅ‚ใฎใƒใ‚นๅœใ‚’ไธ€ๆ‹ฌๅ–ๅพ—                  โ”‚
โ”‚      โ”œโ”€ ไบ‹ๆฅญ่€…ใ‚’ไธ€ๆ‹ฌๅ–ๅพ—                        โ”‚
โ”‚      โ””โ”€ ๅˆ—่ปŠ็จฎๅˆฅใ‚’ไธ€ๆ‹ฌๅ–ๅพ—                      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
    โ”‚
    โ–ผ repository ใƒˆใƒฌใ‚คใƒˆ็ตŒ็”ฑ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Index (src/repository.rs, src/index.rs)       โ”‚
โ”‚  โ””โ”€ MemStationRepository::find_by_id()        โ”‚
โ”‚      โ””โ”€ HashMap ๅ‚็…ง                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
    โ”‚
    โ–ผ Record โ†’ Entity ๅค‰ๆ›
    โ–ผ Entity โ†’ Model ๅค‰ๆ› (use_case/dto/)
    โ–ผ Model โ†’ GraphQL ๅž‹
[Client]

ไธ€ๆ‹ฌๅ–ๅพ—ใฏ N+1 ๅ•้กŒใ‚’้ฟใ‘ใ‚‹ใŸใ‚ใฎใ‚‚ใฎใงใ€ใƒ‡ใƒผใ‚ฟใƒ™ใƒผใ‚นใ‚’ไฝฟใฃใฆใ„ใŸ้ ƒใ‹ใ‚‰ ๅค‰ใˆใฆใ„ใพใ›ใ‚“ใ€‚ใ‚คใƒณใƒกใƒขใƒชใงใ‚ใฃใฆใ‚‚ใ€้ง…ใ”ใจใซ็ดขๅผ•ใ‚’ๅผ•ใ็›ดใ™ใ‚ˆใ‚Šใ€ไธ€ๅบฆใซ ใพใจใ‚ใฆ้›†ใ‚ใ‚‹ใปใ†ใŒ็ด ็›ดใชๅฎŸ่ฃ…ใซใชใ‚Šใพใ™ใ€‚

ใ‚จใƒฉใƒผใฎไผๆ’ญ

DomainError
    โ†“ ? ๆผ”็ฎ—ๅญ (From ใƒˆใƒฌใ‚คใƒˆ)
UseCaseError
    โ†“ ? ๆผ”็ฎ—ๅญ (Display ใ‚’ๅฎŸ่ฃ…ใ—ใŸๅž‹ใ‚’ๅ—ใ‘ๅ–ใ‚‹ async-graphql ใฎๆฑŽ็”จ From ๅฎŸ่ฃ…)
async_graphql::Error
    โ†“
GraphQL ใฎ errors ใƒ•ใ‚ฃใƒผใƒซใƒ‰

repository ใฎๅฎŸ่ฃ…ใŒใชใ„ใƒกใ‚ฝใƒƒใƒ‰ใฏใ€็ฉบใฎ็ตๆžœใงใฏใชใ DomainError ใ‚’่ฟ”ใ™ ๆ–น้‡ใงใ™ (็พๅœจใฏใ€StationRepository::get_route_network ใฎๆ—ขๅฎšๅฎŸ่ฃ…ใŒใ“ใ‚Œใซ ใ‚ใŸใ‚Šใพใ™)ใ€‚้ป™ใฃใฆ็ฉบใฎ็ตๆžœใ‚’่ฟ”ใ™ใจๆญฃๅธธใชๅฟœ็ญ”ใซ่ฆ‹ใˆใฆใ—ใพใ„ใ€ๅฎŸ่ฃ…ๆผใ‚Œใซ ๆฐ—ไป˜ใ‘ใชใ„ใŸใ‚ใงใ™ใ€‚็งป่กŒๆ™‚ใซใฏใ€ๅฎŸ้š›ใซใ“ใฎๆ–น้‡ใฎใŠใ‹ใ’ใงๅฎŸ่ฃ…ๆผใ‚Œใ‚’ 1 ไปถ ๆคœๅ‡บใงใใพใ—ใŸใ€‚


ใƒ‡ใ‚ฃใƒฌใ‚ฏใƒˆใƒชๆง‹้€ 

.
โ”œโ”€โ”€ Cargo.toml            # stationapi-worker (wasm32 ๅฐ‚็”จ) + workspace
โ”œโ”€โ”€ wrangler.jsonc        # staging / production ใฎ่จญๅฎš
โ”œโ”€โ”€ build.rs              # CSV ใ‚’ OUT_DIR ใซ้…็ฝฎใ—ใ€sst.bin ใจ connections.bin ใ‚’็”Ÿๆˆ
โ”œโ”€โ”€ src/                  # Worker ๆœฌไฝ“
โ”‚   โ”œโ”€โ”€ lib.rs            # ใ‚จใƒณใƒ‰ใƒใ‚คใƒณใƒˆ
โ”‚   โ”œโ”€โ”€ index.rs          # ๅŸ‹ใ‚่พผใฟใƒ‡ใƒผใ‚ฟใฎใƒ‘ใƒผใ‚นใจ็ดขๅผ•
โ”‚   โ”œโ”€โ”€ repository.rs     # repository ใƒˆใƒฌใ‚คใƒˆใฎๅฎŸ่ฃ…
โ”‚   โ””โ”€โ”€ graphql/          # GraphQL ใฎๅž‹ใจใƒชใ‚พใƒซใƒ
โ”‚       โ”œโ”€โ”€ query.rs      # 18 ใ‚ฏใ‚จใƒช
โ”‚       โ”œโ”€โ”€ types.rs      # ใ‚ชใƒ–ใ‚ธใ‚งใ‚ฏใƒˆๅž‹
โ”‚       โ”œโ”€โ”€ enums.rs      # ๅˆ—ๆŒ™ๅž‹
โ”‚       โ””โ”€โ”€ scalar.rs     # UInt32 ใ‚นใ‚ซใƒฉใƒผ
โ”‚
โ”œโ”€โ”€ schema/
โ”‚   โ””โ”€โ”€ public.graphql    # ๅ…ฌ้–‹ใ‚นใ‚ญใƒผใƒžใฎๆญฃๆœฌ (CI ใง็ชใๅˆใ‚ใ›ใ‚‹)
โ”‚
โ”œโ”€โ”€ stationapi/           # ใƒ‰ใƒกใ‚คใƒณใจใƒฆใƒผใ‚นใ‚ฑใƒผใ‚น (Worker ใจ preprocessor ใงๅ…ฑๆœ‰)
โ”‚   โ””โ”€โ”€ src/
โ”‚       โ”œโ”€โ”€ domain/
โ”‚       โ”‚   โ”œโ”€โ”€ entity/           # Station / Line / TrainType / Company ...
โ”‚       โ”‚   โ”œโ”€โ”€ repository/       # ๆŠฝ่ฑกใ‚คใƒณใ‚ฟใƒผใƒ•ใ‚งใƒผใ‚น
โ”‚       โ”‚   โ”œโ”€โ”€ arrival_estimation.rs
โ”‚       โ”‚   โ”œโ”€โ”€ legacy_speed_table.rs # ๅ…ƒใฎ่ผƒๆญฃ (connectedRoutes ใจ trainRoute ใฎ Legacy ใŒไฝฟใ†)
โ”‚       โ”‚   โ”œโ”€โ”€ route_search.rs       # ไน—ๆ›็ตŒ่ทฏๆŽข็ดข (RAPTOR)
โ”‚       โ”‚   โ”œโ”€โ”€ route_topology.rs     # ๆ‰€่ฆๆ™‚้–“ใ‚’ๆŒใŸใชใ„็ณป็ตฑ็ถฒ (stationsByName ใฎๅˆฐ้”ๅˆคๅฎš)
โ”‚       โ”‚   โ”œโ”€โ”€ segment_speed_table.rs
โ”‚       โ”‚   โ”œโ”€โ”€ speed_table.rs
โ”‚       โ”‚   โ”œโ”€โ”€ ipa.rs
โ”‚       โ”‚   โ”œโ”€โ”€ romaji.rs
โ”‚       โ”‚   โ””โ”€โ”€ normalize.rs
โ”‚       โ”œโ”€โ”€ use_case/
โ”‚       โ”‚   โ”œโ”€โ”€ interactor/query.rs   # QueryInteractor
โ”‚       โ”‚   โ”œโ”€โ”€ traits/query.rs       # QueryUseCase ใƒˆใƒฌใ‚คใƒˆ
โ”‚       โ”‚   โ””โ”€โ”€ dto/                  # Entity โ†’ Model ๅค‰ๆ›
โ”‚       โ””โ”€โ”€ model.rs                  # API ใŒ่ฟ”ใ™ๅ€คใฎๅž‹
โ”‚
โ”œโ”€โ”€ preprocessor/         # generated/*.csv ใ‚’็”Ÿๆˆใ™ใ‚‹ใƒ“ใƒซใƒ‰ๆ™‚ใƒ„ใƒผใƒซ
โ”‚   โ””โ”€โ”€ src/
โ”‚       โ”œโ”€โ”€ rail.rs       # data/*.csv ใฎ่ชญใฟ่พผใฟใจๅ„้ง…ๅœ่ปŠใฎ็ณป็ตฑ็”Ÿๆˆ
โ”‚       โ”œโ”€โ”€ gtfs/         # GTFS / ODPT ใฎๅ–ๅพ—ใƒป่งฃๆžใƒป็ตฑๅˆ
โ”‚       โ”œโ”€โ”€ track/        # ๅ›ฝๅœŸๆ•ฐๅ€คๆƒ…ๅ ฑใ‹ใ‚‰ใฎ้ง…้–“ใฎ็ทš่ทฏใฎ้•ทใ•
โ”‚       โ”œโ”€โ”€ codes.rs      # ใƒใ‚น็”จใ‚ณใƒผใƒ‰ใฎ็”Ÿๆˆ
โ”‚       โ”œโ”€โ”€ table.rs      # ๅ‡บๅŠ›ใƒ†ใƒผใƒ–ใƒซใฎ่กจ็พ
โ”‚       โ””โ”€โ”€ emit.rs       # CSV ใฎๆ›ธใๅ‡บใ—
โ”‚
โ”œโ”€โ”€ data_validator/       # data/*.csv ใฎๆ•ดๅˆๆ€งใƒใ‚งใƒƒใ‚ฏ
โ”œโ”€โ”€ data/                 # ้‰„้“ใฎๆญฃๆœฌใƒ‡ใƒผใ‚ฟ (CSV) ใจ GTFSใƒปN02 ใฎๅฑ•้–‹ๅ…ˆ
โ”œโ”€โ”€ generated/            # preprocessor ใฎๅ‡บๅŠ› (git ็ฎก็†ๅค–)
โ”œโ”€โ”€ scripts/              # ใƒ‡ใƒผใ‚ฟๆ•ดๅ‚™ใจใ‚นใ‚ญใƒผใƒžๆฏ”่ผƒใฎใ‚นใ‚ฏใƒชใƒ—ใƒˆ
โ””โ”€โ”€ tools/                # IPA ใ‚ซใƒใƒฌใƒƒใ‚ธใฎ็›ฃๆŸป

้‹็”จ

็’ฐๅขƒใฎไฝฟใ„ๅˆ†ใ‘

ไป–ใฎ Worker ใซๅˆใ‚ใ›ใฆใ€env ใ‚’็œ็•ฅใ—ใŸใจใใฏ staging ใซใƒ‡ใƒ—ใƒญใ‚คใ•ใ‚Œใ‚‹ใ‚ˆใ†ใซ ใ—ใฆใ„ใพใ™ใ€‚

make deploy             # wrangler deploy --env=""         -> stationapi-stg
make deploy-production  # wrangler deploy --env production -> stationapi

wrangler 4 ใฏใ€่ค‡ๆ•ฐใฎ็’ฐๅขƒใŒๅฎš็พฉใ•ใ‚Œใฆใ„ใ‚‹็Šถๆ…‹ใง --env ใ‚’็œ็•ฅใ™ใ‚‹ใจ่ญฆๅ‘Šใ‚’ ๅ‡บใ—ใพใ™ใ€‚ใใฎใŸใ‚ใ€staging ใซใƒ‡ใƒ—ใƒญใ‚คใ™ใ‚‹ใจใใ‚‚ --env="" ใ‚’ๆ˜Ž็คบใ—ใฆใ„ใพใ™ใ€‚

ๆณจๆ„็‚น

  • ใƒ‡ใƒผใ‚ฟใ‚’ๆ›ดๆ–ฐใ™ใ‚‹ใŸใณใซๅ†ใƒ‡ใƒ—ใƒญใ‚คใŒๅฟ…่ฆใ€‚ ใƒ‡ใƒผใ‚ฟใ‚’ WASM ใซๅŸ‹ใ‚่พผใ‚“ใง ใ„ใ‚‹ใŸใ‚
  • custom domain ใฏไบŒ้‡ใซ็™ป้Œฒใงใใชใ„ใ€‚ ใƒ‰ใƒกใ‚คใƒณใ‚’ๅˆฅใฎ Worker ใซ็งปใ™ใจใใฏใ€ ๅ…ˆใซๅ…ƒใฎ Worker ใ‹ใ‚‰ๅค–ใ—ใฆใƒ‡ใƒ—ใƒญใ‚คใ—ใฆใŠใๅฟ…่ฆใŒใ‚ใ‚‹
  • generated/ ใฏ git ็ฎก็†ๅค–ใ€‚ ใ‚ฏใƒญใƒผใƒณใ—ใŸ็›ดๅพŒใซใฏๅญ˜ๅœจใ—ใชใ„ใฎใงใ€ make data ใง็”Ÿๆˆใ™ใ‚‹ใ€‚ๅญ˜ๅœจใ—ใชใ„ใพใพ worker-build ใ‚’ๅฎŸ่กŒใ™ใ‚‹ใจ data/*.csv ใซใƒ•ใ‚ฉใƒผใƒซใƒใƒƒใ‚ฏใ—ใ€ๅ„้ง…ๅœ่ปŠใฎ็ณป็ตฑใจใƒใ‚นใŒๆฌ ใ‘ใŸ็Šถๆ…‹ใง ใƒ“ใƒซใƒ‰ใ•ใ‚Œใ‚‹ (่ญฆๅ‘Šใฏๅ‡บใ‚‹)

้–ข้€ฃใƒ‰ใ‚ญใƒฅใƒกใƒณใƒˆ