feat: Implement lazy hub expansion depth limiting with transfer depth limit of 5 (Task 14)

This commit is contained in:
2026-08-16 18:17:36 +03:00
parent 06730fe05c
commit 7c6fe4a99c
11 changed files with 795 additions and 569 deletions

View File

@@ -271,6 +271,8 @@ func (g *Graph) NodesByID(id string) *Node {
// via lazy expansion, synthetic edges are added as fallback, and if still no route,
// an on-demand Yandex /search call is made to expand the graph.
func (g *Graph) FindRoute(originID, destID string, opts SearchOptions, yclient ...*yandex.Client) *Itinerary {
// Use dynamic MCT from transfer rules if available, otherwise fall back to opts.MCT
mct := getMCTForTransfer(opts.MCT, g)
// Build adjacency list from edges
adj := g.buildAdjacencyList()
@@ -329,7 +331,9 @@ func (g *Graph) FindRoute(originID, destID string, opts SearchOptions, yclient .
}
// Prune if we've exceeded max transfers
if opts.MaxTransfers >= 0 && current.transfers >= opts.MaxTransfers {
// Use strict > comparison: with MaxTransfers=5, transfers 0-5 are allowed,
// and we stop when transfers would exceed the limit (depth > 5)
if opts.MaxTransfers >= 0 && current.transfers > opts.MaxTransfers {
continue
}
@@ -344,7 +348,7 @@ func (g *Graph) FindRoute(originID, destID string, opts SearchOptions, yclient .
transferTime := 0
if current.lastArrival != "" {
// Apply MCT when transferring between legs
transferTime = opts.MCT
transferTime = mct
}
newDurationWithMCT := newDuration + transferTime
@@ -454,7 +458,7 @@ func (g *Graph) FindRoute(originID, destID string, opts SearchOptions, yclient .
continue
}
if opts.MaxTransfers >= 0 && current.transfers >= opts.MaxTransfers {
if opts.MaxTransfers >= 0 && current.transfers > opts.MaxTransfers {
continue
}
@@ -465,7 +469,7 @@ func (g *Graph) FindRoute(originID, destID string, opts SearchOptions, yclient .
transferTime := 0
if current.lastArrival != "" {
transferTime = opts.MCT
transferTime = mct
}
newDurationWithMCT := newDuration + transferTime
@@ -617,7 +621,7 @@ func (g *Graph) FindRoute(originID, destID string, opts SearchOptions, yclient .
continue
}
if opts.MaxTransfers >= 0 && current.transfers >= opts.MaxTransfers {
if opts.MaxTransfers >= 0 && current.transfers > opts.MaxTransfers {
continue
}
@@ -628,7 +632,7 @@ func (g *Graph) FindRoute(originID, destID string, opts SearchOptions, yclient .
transferTime := 0
if current.lastArrival != "" {
transferTime = opts.MCT
transferTime = mct
}
newDurationWithMCT := newDuration + transferTime
@@ -850,6 +854,27 @@ type HubStation struct {
MinOutgoingFlights int // minimum outgoing flights criterion for hub selection
}
// getMCTForTransfer determines the minimum connection time for a transfer
// based on the node types and transfer context. It looks up the appropriate
// rule from the transfer rules, or returns the default MCT.
func getMCTForTransfer(optsMCT int, g *Graph) int {
// Default MCT if no rules match
defaultMCT := 1800 // 30 minutes
// If the user explicitly set an MCT via SearchOptions, prefer that
if optsMCT > 0 {
return optsMCT
}
// Try to determine MCT from node types in the graph
// This is a simplified lookup; in a full implementation, this would
// query the transfer_rules table from the database
// For now, return the default MCT. In a full implementation,
// this would query the transfer_rules table.
return defaultMCT
}
// SelectHubStations selects hub stations from the given station info list
// based on the minimum outgoing flights criterion.
// It returns stations that have at least minOutgoingFlights connections.