feat(geo): graph-based hubs for product
All checks were successful
Build Docker Image / build (push) Successful in 1m52s
All checks were successful
Build Docker Image / build (push) Successful in 1m52s
This commit is contained in:
@@ -334,6 +334,7 @@ class Query(graphene.ObjectType):
|
||||
radius=graphene.Float(default_value=1000, description="Search radius in km"),
|
||||
product_uuid=graphene.String(description="Filter hubs by product availability"),
|
||||
source_uuid=graphene.String(description="Source node UUID for graph-based nearest hubs"),
|
||||
use_graph=graphene.Boolean(default_value=False, description="Use graph-based reachability for product filter"),
|
||||
limit=graphene.Int(default_value=12, description="Max results"),
|
||||
description="Find nearest hubs to coordinates (optionally filtered by product)",
|
||||
)
|
||||
@@ -1166,6 +1167,58 @@ class Query(graphene.ObjectType):
|
||||
logger.error("Error getting hubs for product: %s", e)
|
||||
return []
|
||||
|
||||
def resolve_hubs_for_product_graph(self, info, product_uuid, limit=500):
|
||||
"""Get hubs that can be reached by graph routes for a product."""
|
||||
db = get_db()
|
||||
ensure_graph()
|
||||
|
||||
aql = """
|
||||
FOR hub IN nodes
|
||||
FILTER hub.node_type == 'logistics' OR hub.node_type == null
|
||||
LET types = hub.transport_types != null ? hub.transport_types : []
|
||||
FILTER ('rail' IN types) OR ('sea' IN types)
|
||||
FILTER hub.latitude != null AND hub.longitude != null
|
||||
RETURN hub
|
||||
"""
|
||||
try:
|
||||
cursor = db.aql.execute(aql)
|
||||
hubs_with_distance = []
|
||||
for hub in cursor:
|
||||
hub_uuid = hub.get('_key')
|
||||
if not hub_uuid:
|
||||
continue
|
||||
try:
|
||||
routes = self.resolve_offers_by_hub(info, hub_uuid, product_uuid, limit=1)
|
||||
except Exception as route_error:
|
||||
logger.error("Error resolving offers for hub %s: %s", hub_uuid, route_error)
|
||||
continue
|
||||
if not routes:
|
||||
continue
|
||||
distance_km = routes[0].distance_km if routes[0] else None
|
||||
hubs_with_distance.append((distance_km, hub))
|
||||
|
||||
# Sort by graph distance when available
|
||||
hubs_with_distance.sort(key=lambda item: (item[0] is None, item[0] or 0))
|
||||
|
||||
hubs = []
|
||||
for distance_km, hub in hubs_with_distance[:limit]:
|
||||
hubs.append(NodeType(
|
||||
uuid=hub.get('_key'),
|
||||
name=hub.get('name'),
|
||||
latitude=hub.get('latitude'),
|
||||
longitude=hub.get('longitude'),
|
||||
country=hub.get('country'),
|
||||
country_code=hub.get('country_code'),
|
||||
transport_types=hub.get('transport_types'),
|
||||
distance_km=distance_km,
|
||||
))
|
||||
|
||||
logger.info("Found %d graph-reachable hubs for product %s", len(hubs), product_uuid)
|
||||
return hubs
|
||||
except Exception as e:
|
||||
logger.error("Error getting graph hubs for product %s: %s", product_uuid, e)
|
||||
return []
|
||||
|
||||
def resolve_offers_by_hub(self, info, hub_uuid, product_uuid, limit=10):
|
||||
"""
|
||||
Get offers for a product with routes to hub.
|
||||
@@ -1510,7 +1563,7 @@ class Query(graphene.ObjectType):
|
||||
logger.info("No route found from offer %s to hub %s", offer_uuid, hub_uuid)
|
||||
return None
|
||||
|
||||
def resolve_nearest_hubs(self, info, lat, lon, radius=1000, product_uuid=None, source_uuid=None, limit=12):
|
||||
def resolve_nearest_hubs(self, info, lat, lon, radius=1000, product_uuid=None, source_uuid=None, use_graph=False, limit=12):
|
||||
"""Find nearest hubs to coordinates, optionally filtered by product availability."""
|
||||
db = get_db()
|
||||
|
||||
@@ -1601,6 +1654,9 @@ class Query(graphene.ObjectType):
|
||||
))
|
||||
return hubs
|
||||
|
||||
if product_uuid and use_graph:
|
||||
return self.resolve_hubs_for_product_graph(info, product_uuid, limit=limit)
|
||||
|
||||
if product_uuid:
|
||||
# Find hubs that have offers for this product within radius
|
||||
aql = """
|
||||
|
||||
Reference in New Issue
Block a user