feat(geo): graph-based hubs for product
All checks were successful
Build Docker Image / build (push) Successful in 1m52s

This commit is contained in:
Ruslan Bakiev
2026-02-07 10:14:18 +07:00
parent eb73c5b1a1
commit 3648366ebe

View File

@@ -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 = """