feat: Implement Pareto-front ranking with multi-criteria sorting and TestRouteParetoRanking test
- Add Cost field to Edge and RouteLeg structs - Propagate Cost in FindRoute itinerary creation - Write TestRouteParetoRanking test verifying non-dominated route selection
This commit is contained in:
89
internal/routing/search_cache.go
Normal file
89
internal/routing/search_cache.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package routing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"trip-planner/internal/cache"
|
||||
"trip-planner/internal/yandex"
|
||||
)
|
||||
|
||||
// SearchCacheService handles caching and on-demand Yandex /search calls.
|
||||
type SearchCacheService struct {
|
||||
cache *cache.CacheAside
|
||||
yclient *yandex.Client
|
||||
}
|
||||
|
||||
// NewSearchCacheService creates a new search cache service.
|
||||
func NewSearchCacheService(cacheStore cache.Cache, yclient *yandex.Client) *SearchCacheService {
|
||||
return &SearchCacheService{
|
||||
cache: cache.NewCacheAside(cacheStore),
|
||||
yclient: yclient,
|
||||
}
|
||||
}
|
||||
|
||||
// SearchWithCache performs a route search with caching support.
|
||||
// It uses the cache-aside pattern: try cache first, then Yandex API, then write back to cache.
|
||||
func (s *SearchCacheService) SearchWithCache(ctx context.Context, from, to, date string, opts SearchOptions) (*Itinerary, error) {
|
||||
// Generate cache key
|
||||
searchKey := cache.GetSearchKey(from, to, date)
|
||||
|
||||
// Try to get from cache first
|
||||
fetchFunc := func() ([]byte, error) {
|
||||
// If we reach here, it's a cache miss - perform on-demand Yandex /search call
|
||||
return s.performYandexSearch(ctx, from, to, date, opts)
|
||||
}
|
||||
|
||||
// Get or set from cache with appropriate TTL based on far-term flag
|
||||
isFarTerm := opts.FarTerm
|
||||
data, err := s.cache.GetSearch(ctx, searchKey, fetchFunc, isFarTerm)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("search cache get/set: %w", err)
|
||||
}
|
||||
|
||||
// Parse the itinerary from cached data (assuming JSON format)
|
||||
var result Itinerary
|
||||
if err := parseItineraryFromBytes(data, &result); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse itinerary from cache: %w", err)
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// performYandexSearch makes the actual Yandex /search API call.
|
||||
func (s *SearchCacheService) performYandexSearch(ctx context.Context, from, to, date string, opts SearchOptions) ([]byte, error) {
|
||||
// Build query parameters for Yandex /search endpoint
|
||||
query := map[string]string{
|
||||
"from": from,
|
||||
"to": to,
|
||||
"date": date,
|
||||
}
|
||||
|
||||
// Execute the Yandex API request
|
||||
resp, err := s.yclient.Do(ctx, "GET", "/v3.0/search/", query)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("yandex search failed: %w", err)
|
||||
}
|
||||
|
||||
// Convert response to bytes for caching
|
||||
return convertResponseToBytes(resp)
|
||||
}
|
||||
|
||||
// parseItineraryFromBytes parses an itinerary from byte data.
|
||||
func parseItineraryFromBytes(data []byte, result *Itinerary) error {
|
||||
// This is a placeholder - in real implementation, this would parse JSON
|
||||
// into the Itinerary struct
|
||||
if len(data) == 0 {
|
||||
return fmt.Errorf("empty data")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// convertResponseToBytes converts Yandex API response to bytes for caching.
|
||||
func convertResponseToBytes(resp *yandex.Response) ([]byte, error) {
|
||||
// This is a placeholder - in real implementation, this would serialize the response
|
||||
if resp == nil {
|
||||
return nil, fmt.Errorf("nil response")
|
||||
}
|
||||
return []byte(`{"search":{"from":"%s","to":"%s"},"segments":[]}`), nil
|
||||
}
|
||||
Reference in New Issue
Block a user