Unify hub offer queries and drop radius filters
All checks were successful
Build Docker Image / build (push) Successful in 2m7s
All checks were successful
Build Docker Image / build (push) Successful in 2m7s
This commit is contained in:
@@ -311,7 +311,7 @@ class Query(graphene.ObjectType):
|
||||
description="Get hubs where a product is available nearby",
|
||||
)
|
||||
|
||||
offers_by_hub = graphene.List(
|
||||
offers_for_hub = graphene.List(
|
||||
ProductRouteOptionType,
|
||||
hub_uuid=graphene.String(required=True),
|
||||
product_uuid=graphene.String(required=True),
|
||||
@@ -319,13 +319,6 @@ class Query(graphene.ObjectType):
|
||||
description="Get offers for a product with routes to hub (auto → rail* → auto)",
|
||||
)
|
||||
|
||||
offer_to_hub = graphene.Field(
|
||||
ProductRouteOptionType,
|
||||
offer_uuid=graphene.String(required=True),
|
||||
hub_uuid=graphene.String(required=True),
|
||||
description="Get route from a specific offer to hub",
|
||||
)
|
||||
|
||||
# New unified endpoints (coordinate-based)
|
||||
nearest_hubs = graphene.List(
|
||||
NodeType,
|
||||
@@ -1124,48 +1117,8 @@ class Query(graphene.ObjectType):
|
||||
return []
|
||||
|
||||
def resolve_hubs_for_product(self, info, product_uuid, radius_km=500):
|
||||
"""Get hubs that have this product available within radius."""
|
||||
db = get_db()
|
||||
aql = """
|
||||
FOR offer IN nodes
|
||||
FILTER offer.node_type == 'offer'
|
||||
FILTER offer.product_uuid == @product_uuid
|
||||
FILTER offer.latitude != null AND offer.longitude != null
|
||||
FOR hub IN nodes
|
||||
FILTER hub.node_type == 'logistics' OR hub.node_type == null
|
||||
FILTER hub.latitude != null AND hub.longitude != null
|
||||
LET dist = DISTANCE(offer.latitude, offer.longitude, hub.latitude, hub.longitude) / 1000
|
||||
FILTER dist <= @radius_km
|
||||
COLLECT hub_uuid = hub._key, hub_name = hub.name,
|
||||
hub_lat = hub.latitude, hub_lon = hub.longitude,
|
||||
hub_country = hub.country, hub_country_code = hub.country_code,
|
||||
hub_transport = hub.transport_types
|
||||
RETURN {
|
||||
uuid: hub_uuid,
|
||||
name: hub_name,
|
||||
latitude: hub_lat,
|
||||
longitude: hub_lon,
|
||||
country: hub_country,
|
||||
country_code: hub_country_code,
|
||||
transport_types: hub_transport
|
||||
}
|
||||
"""
|
||||
try:
|
||||
cursor = db.aql.execute(aql, bind_vars={'product_uuid': product_uuid, 'radius_km': radius_km})
|
||||
hubs = [NodeType(
|
||||
uuid=h['uuid'],
|
||||
name=h['name'],
|
||||
latitude=h['latitude'],
|
||||
longitude=h['longitude'],
|
||||
country=h['country'],
|
||||
country_code=h.get('country_code'),
|
||||
transport_types=h.get('transport_types')
|
||||
) for h in cursor]
|
||||
logger.info("Found %d hubs for product %s", len(hubs), product_uuid)
|
||||
return hubs
|
||||
except Exception as e:
|
||||
logger.error("Error getting hubs for product: %s", e)
|
||||
return []
|
||||
"""Get hubs that can accept this product (graph-based)."""
|
||||
return self.resolve_hubs_for_product_graph(info, product_uuid)
|
||||
|
||||
def resolve_hubs_for_product_graph(self, info, product_uuid, limit=500):
|
||||
"""Get hubs that can be reached by graph routes for a product."""
|
||||
@@ -1219,7 +1172,11 @@ class Query(graphene.ObjectType):
|
||||
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):
|
||||
def resolve_offers_for_hub(self, info, hub_uuid, product_uuid, limit=10):
|
||||
"""Alias for offers by hub (graph-based)."""
|
||||
return self.resolve_offers_by_hub(info, hub_uuid, product_uuid, limit=limit)
|
||||
|
||||
def resolve_offers_by_hub(self, info, hub_uuid, product_uuid=None, limit=10):
|
||||
"""
|
||||
Get offers for a product with routes to hub.
|
||||
Uses phase-based routing: auto → rail* → auto
|
||||
@@ -1335,9 +1292,11 @@ class Query(graphene.ObjectType):
|
||||
if (node_key, phase) in visited and cost > visited[(node_key, phase)]:
|
||||
continue
|
||||
|
||||
# Found an offer for the product
|
||||
# Found an offer (optionally filtered by product)
|
||||
node_doc = node_docs.get(node_key)
|
||||
if node_doc and node_doc.get('product_uuid') == product_uuid:
|
||||
if node_doc and node_doc.get('node_type') == 'offer' and (
|
||||
product_uuid is None or node_doc.get('product_uuid') == product_uuid
|
||||
):
|
||||
path_edges = []
|
||||
state = (node_key, phase)
|
||||
current_key = node_key
|
||||
@@ -1393,176 +1352,15 @@ class Query(graphene.ObjectType):
|
||||
predecessors[state_key] = ((node_key, phase), neighbor)
|
||||
|
||||
if not found_routes:
|
||||
logger.info("No offers found for product %s near hub %s", product_uuid, hub_uuid)
|
||||
logger.info("No offers found near hub %s", hub_uuid)
|
||||
return []
|
||||
|
||||
logger.info("Found %d offers for product %s near hub %s", len(found_routes), product_uuid, hub_uuid)
|
||||
if product_uuid:
|
||||
logger.info("Found %d offers for product %s near hub %s", len(found_routes), product_uuid, hub_uuid)
|
||||
else:
|
||||
logger.info("Found %d offers near hub %s", len(found_routes), hub_uuid)
|
||||
return found_routes
|
||||
|
||||
def resolve_offer_to_hub(self, info, offer_uuid, hub_uuid):
|
||||
"""
|
||||
Get route from a specific offer to hub.
|
||||
Uses phase-based routing: auto → rail* → auto
|
||||
"""
|
||||
db = get_db()
|
||||
ensure_graph()
|
||||
nodes_col = db.collection('nodes')
|
||||
|
||||
offer = nodes_col.get(offer_uuid)
|
||||
if not offer:
|
||||
logger.info("Offer %s not found", offer_uuid)
|
||||
return None
|
||||
|
||||
hub = nodes_col.get(hub_uuid)
|
||||
if not hub:
|
||||
logger.info("Hub %s not found", hub_uuid)
|
||||
return None
|
||||
|
||||
hub_lat = hub.get('latitude')
|
||||
hub_lon = hub.get('longitude')
|
||||
offer_lat = offer.get('latitude')
|
||||
offer_lon = offer.get('longitude')
|
||||
|
||||
# Phase-based routing from hub to offer
|
||||
def allowed_next_phase(current_phase, transport_type):
|
||||
if current_phase == 'end_auto':
|
||||
if transport_type == 'offer':
|
||||
return 'offer'
|
||||
if transport_type == 'auto':
|
||||
return 'end_auto_done'
|
||||
if transport_type == 'rail':
|
||||
return 'rail'
|
||||
return None
|
||||
if current_phase == 'end_auto_done':
|
||||
if transport_type == 'offer':
|
||||
return 'offer'
|
||||
if transport_type == 'rail':
|
||||
return 'rail'
|
||||
return None
|
||||
if current_phase == 'rail':
|
||||
if transport_type == 'offer':
|
||||
return 'offer'
|
||||
if transport_type == 'rail':
|
||||
return 'rail'
|
||||
if transport_type == 'auto':
|
||||
return 'start_auto_done'
|
||||
return None
|
||||
if current_phase == 'start_auto_done':
|
||||
if transport_type == 'offer':
|
||||
return 'offer'
|
||||
return None
|
||||
return None
|
||||
|
||||
def fetch_neighbors(node_key, phase):
|
||||
if phase == 'end_auto':
|
||||
types = ['auto', 'rail', 'offer']
|
||||
elif phase == 'end_auto_done':
|
||||
types = ['rail', 'offer']
|
||||
elif phase == 'rail':
|
||||
types = ['rail', 'auto', 'offer']
|
||||
elif phase == 'start_auto_done':
|
||||
types = ['offer']
|
||||
else:
|
||||
types = ['offer']
|
||||
|
||||
aql = """
|
||||
FOR edge IN edges
|
||||
FILTER edge.transport_type IN @types
|
||||
FILTER edge._from == @node_id OR edge._to == @node_id
|
||||
LET neighbor_id = edge._from == @node_id ? edge._to : edge._from
|
||||
LET neighbor = DOCUMENT(neighbor_id)
|
||||
FILTER neighbor != null
|
||||
RETURN {
|
||||
neighbor_key: neighbor._key,
|
||||
neighbor_doc: neighbor,
|
||||
from_id: edge._from,
|
||||
to_id: edge._to,
|
||||
transport_type: edge.transport_type,
|
||||
distance_km: edge.distance_km,
|
||||
travel_time_seconds: edge.travel_time_seconds
|
||||
}
|
||||
"""
|
||||
cursor = db.aql.execute(
|
||||
aql,
|
||||
bind_vars={
|
||||
'node_id': f"nodes/{node_key}",
|
||||
'types': types,
|
||||
},
|
||||
)
|
||||
return list(cursor)
|
||||
|
||||
queue = []
|
||||
counter = 0
|
||||
heapq.heappush(queue, (0, counter, hub_uuid, 'end_auto'))
|
||||
|
||||
visited = {}
|
||||
predecessors = {}
|
||||
node_docs = {hub_uuid: hub, offer_uuid: offer}
|
||||
|
||||
expansions = 0
|
||||
|
||||
while queue and expansions < Query.MAX_EXPANSIONS:
|
||||
cost, _, node_key, phase = heapq.heappop(queue)
|
||||
|
||||
if (node_key, phase) in visited and cost > visited[(node_key, phase)]:
|
||||
continue
|
||||
|
||||
# Found the specific offer
|
||||
if node_key == offer_uuid:
|
||||
path_edges = []
|
||||
state = (node_key, phase)
|
||||
current_key = node_key
|
||||
while state in predecessors:
|
||||
prev_state, edge_info = predecessors[state]
|
||||
prev_key = prev_state[0]
|
||||
path_edges.append((current_key, prev_key, edge_info))
|
||||
state = prev_state
|
||||
current_key = prev_key
|
||||
|
||||
route = _build_route_from_edges(path_edges, node_docs)
|
||||
distance_km = None
|
||||
if offer_lat is not None and offer_lon is not None and hub_lat is not None and hub_lon is not None:
|
||||
distance_km = _distance_km(offer_lat, offer_lon, hub_lat, hub_lon)
|
||||
|
||||
return ProductRouteOptionType(
|
||||
source_uuid=offer_uuid,
|
||||
source_name=offer.get('name') or offer.get('product_name'),
|
||||
source_lat=offer_lat,
|
||||
source_lon=offer_lon,
|
||||
distance_km=distance_km,
|
||||
routes=[route] if route else [],
|
||||
)
|
||||
|
||||
neighbors = fetch_neighbors(node_key, phase)
|
||||
expansions += 1
|
||||
|
||||
for neighbor in neighbors:
|
||||
transport_type = neighbor.get('transport_type')
|
||||
next_phase = allowed_next_phase(phase, transport_type)
|
||||
if next_phase is None:
|
||||
continue
|
||||
|
||||
travel_time = neighbor.get('travel_time_seconds')
|
||||
distance_km = neighbor.get('distance_km')
|
||||
|
||||
neighbor_key = neighbor.get('neighbor_key')
|
||||
node_docs[neighbor_key] = neighbor.get('neighbor_doc')
|
||||
|
||||
step_cost = travel_time if travel_time is not None else (distance_km or 0)
|
||||
new_cost = cost + step_cost
|
||||
|
||||
state_key = (neighbor_key, next_phase)
|
||||
if state_key in visited and new_cost >= visited[state_key]:
|
||||
continue
|
||||
|
||||
visited[state_key] = new_cost
|
||||
counter += 1
|
||||
heapq.heappush(queue, (new_cost, counter, neighbor_key, next_phase))
|
||||
predecessors[state_key] = ((node_key, phase), neighbor)
|
||||
|
||||
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, use_graph=False, limit=12):
|
||||
"""Find nearest hubs to coordinates, optionally filtered by product availability."""
|
||||
db = get_db()
|
||||
@@ -1654,51 +1452,23 @@ class Query(graphene.ObjectType):
|
||||
))
|
||||
return hubs
|
||||
|
||||
if product_uuid and use_graph:
|
||||
if product_uuid:
|
||||
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 = """
|
||||
FOR offer IN nodes
|
||||
FILTER offer.node_type == 'offer'
|
||||
FILTER offer.product_uuid == @product_uuid
|
||||
FILTER offer.latitude != null AND offer.longitude != null
|
||||
LET dist_to_offer = DISTANCE(offer.latitude, offer.longitude, @lat, @lon) / 1000
|
||||
FILTER dist_to_offer <= @radius
|
||||
|
||||
FOR hub IN nodes
|
||||
FILTER hub.node_type == 'logistics' OR hub.node_type == null
|
||||
FILTER hub.product_uuid == 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
|
||||
LET dist_to_hub = DISTANCE(hub.latitude, hub.longitude, @lat, @lon) / 1000
|
||||
FILTER dist_to_hub <= @radius
|
||||
COLLECT hub_uuid = hub._key INTO hub_group
|
||||
LET first_hub = FIRST(hub_group)[0].hub
|
||||
LET hub_dist = DISTANCE(first_hub.latitude, first_hub.longitude, @lat, @lon) / 1000
|
||||
SORT hub_dist ASC
|
||||
LIMIT @limit
|
||||
RETURN MERGE(first_hub, {_key: hub_uuid, distance_km: hub_dist})
|
||||
"""
|
||||
bind_vars = {'lat': lat, 'lon': lon, 'radius': radius, 'product_uuid': product_uuid, 'limit': limit}
|
||||
else:
|
||||
# Simple nearest hubs search
|
||||
aql = """
|
||||
FOR hub IN nodes
|
||||
FILTER hub.node_type == 'logistics' OR hub.node_type == null
|
||||
FILTER hub.product_uuid == 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
|
||||
LET dist = DISTANCE(hub.latitude, hub.longitude, @lat, @lon) / 1000
|
||||
FILTER dist <= @radius
|
||||
SORT dist ASC
|
||||
LIMIT @limit
|
||||
RETURN MERGE(hub, {distance_km: dist})
|
||||
"""
|
||||
bind_vars = {'lat': lat, 'lon': lon, 'radius': radius, 'limit': limit}
|
||||
# Simple nearest hubs search (no radius filtering)
|
||||
aql = """
|
||||
FOR hub IN nodes
|
||||
FILTER hub.node_type == 'logistics' OR hub.node_type == null
|
||||
FILTER hub.product_uuid == 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
|
||||
LET dist = DISTANCE(hub.latitude, hub.longitude, @lat, @lon) / 1000
|
||||
SORT dist ASC
|
||||
LIMIT @limit
|
||||
RETURN MERGE(hub, {distance_km: dist})
|
||||
"""
|
||||
bind_vars = {'lat': lat, 'lon': lon, 'limit': limit}
|
||||
|
||||
try:
|
||||
cursor = db.aql.execute(aql, bind_vars=bind_vars)
|
||||
@@ -1716,7 +1486,7 @@ class Query(graphene.ObjectType):
|
||||
edges=[],
|
||||
distance_km=node.get('distance_km'),
|
||||
))
|
||||
logger.info("Found %d hubs near (%.3f, %.3f) within %d km", len(hubs), lat, lon, radius)
|
||||
logger.info("Found %d hubs near (%.3f, %.3f)", len(hubs), lat, lon)
|
||||
return hubs
|
||||
except Exception as e:
|
||||
logger.error("Error finding nearest hubs: %s", e)
|
||||
@@ -1727,91 +1497,41 @@ class Query(graphene.ObjectType):
|
||||
db = get_db()
|
||||
ensure_graph()
|
||||
|
||||
# If hub_uuid + product_uuid provided, use graph search to return only offers with routes.
|
||||
if hub_uuid and product_uuid:
|
||||
try:
|
||||
nodes_col = db.collection('nodes')
|
||||
expanded_limit = max(limit * 5, limit)
|
||||
route_options = Query.resolve_offers_by_hub(
|
||||
Query, info, hub_uuid, product_uuid, expanded_limit
|
||||
)
|
||||
offers = []
|
||||
for option in route_options or []:
|
||||
if not option.routes:
|
||||
continue
|
||||
node = nodes_col.get(option.source_uuid)
|
||||
if not node:
|
||||
continue
|
||||
offers.append(OfferWithRouteType(
|
||||
uuid=node['_key'],
|
||||
product_uuid=node.get('product_uuid'),
|
||||
product_name=node.get('product_name'),
|
||||
supplier_uuid=node.get('supplier_uuid'),
|
||||
supplier_name=node.get('supplier_name'),
|
||||
latitude=node.get('latitude'),
|
||||
longitude=node.get('longitude'),
|
||||
country=node.get('country'),
|
||||
country_code=node.get('country_code'),
|
||||
price_per_unit=node.get('price_per_unit'),
|
||||
currency=node.get('currency'),
|
||||
quantity=node.get('quantity'),
|
||||
unit=node.get('unit'),
|
||||
distance_km=option.distance_km,
|
||||
routes=option.routes,
|
||||
))
|
||||
if len(offers) >= limit:
|
||||
break
|
||||
logger.info("Found %d offers by graph for hub %s", len(offers), hub_uuid)
|
||||
return offers
|
||||
except Exception as e:
|
||||
logger.error("Error finding offers by hub %s: %s", hub_uuid, e)
|
||||
return []
|
||||
|
||||
aql = """
|
||||
FOR offer IN nodes
|
||||
FILTER offer.node_type == 'offer'
|
||||
FILTER offer.product_uuid != null
|
||||
FILTER offer.latitude != null AND offer.longitude != null
|
||||
"""
|
||||
|
||||
if product_uuid:
|
||||
aql += " FILTER offer.product_uuid == @product_uuid\n"
|
||||
|
||||
aql += """
|
||||
LET dist = DISTANCE(offer.latitude, offer.longitude, @lat, @lon) / 1000
|
||||
FILTER dist <= @radius
|
||||
SORT dist ASC
|
||||
LIMIT @limit
|
||||
RETURN MERGE(offer, {distance_km: dist})
|
||||
"""
|
||||
|
||||
bind_vars = {'lat': lat, 'lon': lon, 'radius': radius, 'limit': limit}
|
||||
if product_uuid:
|
||||
bind_vars['product_uuid'] = product_uuid
|
||||
|
||||
try:
|
||||
cursor = db.aql.execute(aql, bind_vars=bind_vars)
|
||||
offer_nodes = list(cursor)
|
||||
logger.info("Found %d offers near (%.3f, %.3f) within %d km", len(offer_nodes), lat, lon, radius)
|
||||
nodes_col = db.collection('nodes')
|
||||
|
||||
# If hub_uuid provided, calculate routes for each offer
|
||||
if hub_uuid:
|
||||
nodes_col = db.collection('nodes')
|
||||
hub = nodes_col.get(hub_uuid)
|
||||
if not hub:
|
||||
logger.warning("Hub %s not found for route calculation", hub_uuid)
|
||||
hub_uuid = None
|
||||
# If no hub_uuid provided, snap to nearest hub by coordinates.
|
||||
if not hub_uuid:
|
||||
aql_hub = """
|
||||
FOR hub IN nodes
|
||||
FILTER hub.node_type == 'logistics' OR hub.node_type == null
|
||||
FILTER hub.product_uuid == 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
|
||||
LET dist = DISTANCE(hub.latitude, hub.longitude, @lat, @lon) / 1000
|
||||
SORT dist ASC
|
||||
LIMIT 1
|
||||
RETURN hub
|
||||
"""
|
||||
cursor = db.aql.execute(aql_hub, bind_vars={'lat': lat, 'lon': lon})
|
||||
hubs = list(cursor)
|
||||
if not hubs:
|
||||
logger.info("No hub found near coordinates (%.3f, %.3f)", lat, lon)
|
||||
return []
|
||||
hub_uuid = hubs[0]['_key']
|
||||
|
||||
expanded_limit = max(limit * 5, limit)
|
||||
route_options = Query.resolve_offers_by_hub(
|
||||
Query, info, hub_uuid, product_uuid, expanded_limit
|
||||
)
|
||||
offers = []
|
||||
for node in offer_nodes:
|
||||
routes = []
|
||||
|
||||
# Calculate route to hub if hub_uuid provided
|
||||
if hub_uuid:
|
||||
route_result = Query.resolve_offer_to_hub(Query, info, node['_key'], hub_uuid)
|
||||
if route_result and route_result.routes:
|
||||
routes = route_result.routes
|
||||
|
||||
for option in route_options or []:
|
||||
if not option.routes:
|
||||
continue
|
||||
node = nodes_col.get(option.source_uuid)
|
||||
if not node:
|
||||
continue
|
||||
offers.append(OfferWithRouteType(
|
||||
uuid=node['_key'],
|
||||
product_uuid=node.get('product_uuid'),
|
||||
@@ -1826,10 +1546,12 @@ class Query(graphene.ObjectType):
|
||||
currency=node.get('currency'),
|
||||
quantity=node.get('quantity'),
|
||||
unit=node.get('unit'),
|
||||
distance_km=node.get('distance_km'),
|
||||
routes=routes,
|
||||
distance_km=option.distance_km,
|
||||
routes=option.routes,
|
||||
))
|
||||
|
||||
if len(offers) >= limit:
|
||||
break
|
||||
logger.info("Found %d offers by graph for hub %s", len(offers), hub_uuid)
|
||||
return offers
|
||||
except Exception as e:
|
||||
logger.error("Error finding nearest offers: %s", e)
|
||||
@@ -1920,45 +1642,63 @@ class Query(graphene.ObjectType):
|
||||
"""Find nearest suppliers to coordinates, optionally filtered by product."""
|
||||
db = get_db()
|
||||
|
||||
aql = """
|
||||
FOR offer IN nodes
|
||||
FILTER offer.node_type == 'offer'
|
||||
FILTER offer.supplier_uuid != null
|
||||
FILTER offer.latitude != null AND offer.longitude != null
|
||||
"""
|
||||
|
||||
if product_uuid:
|
||||
aql += " FILTER offer.product_uuid == @product_uuid\n"
|
||||
aql = """
|
||||
FOR offer IN nodes
|
||||
FILTER offer.node_type == 'offer'
|
||||
FILTER offer.supplier_uuid != null
|
||||
FILTER offer.product_uuid == @product_uuid
|
||||
COLLECT supplier_uuid = offer.supplier_uuid INTO offers
|
||||
LET first_offer = FIRST(offers).offer
|
||||
|
||||
aql += """
|
||||
LET dist = DISTANCE(offer.latitude, offer.longitude, @lat, @lon) / 1000
|
||||
FILTER dist <= @radius
|
||||
COLLECT supplier_uuid = offer.supplier_uuid INTO offers
|
||||
LET first_offer = FIRST(offers).offer
|
||||
LET supplier_dist = DISTANCE(first_offer.latitude, first_offer.longitude, @lat, @lon) / 1000
|
||||
// Try to find supplier node for full info
|
||||
LET supplier_node = FIRST(
|
||||
FOR s IN nodes
|
||||
FILTER s._key == supplier_uuid
|
||||
FILTER s.node_type == 'supplier'
|
||||
RETURN s
|
||||
)
|
||||
|
||||
// Try to find supplier node for full info
|
||||
LET supplier_node = FIRST(
|
||||
FOR s IN nodes
|
||||
FILTER s._key == supplier_uuid
|
||||
FILTER s.node_type == 'supplier'
|
||||
RETURN s
|
||||
)
|
||||
LIMIT @limit
|
||||
RETURN {
|
||||
uuid: supplier_uuid,
|
||||
name: supplier_node != null ? supplier_node.name : first_offer.supplier_name,
|
||||
latitude: supplier_node != null ? supplier_node.latitude : first_offer.latitude,
|
||||
longitude: supplier_node != null ? supplier_node.longitude : first_offer.longitude,
|
||||
distance_km: null
|
||||
}
|
||||
"""
|
||||
bind_vars = {'product_uuid': product_uuid, 'limit': limit}
|
||||
else:
|
||||
aql = """
|
||||
FOR offer IN nodes
|
||||
FILTER offer.node_type == 'offer'
|
||||
FILTER offer.supplier_uuid != null
|
||||
FILTER offer.latitude != null AND offer.longitude != null
|
||||
LET dist = DISTANCE(offer.latitude, offer.longitude, @lat, @lon) / 1000
|
||||
COLLECT supplier_uuid = offer.supplier_uuid INTO offers
|
||||
LET first_offer = FIRST(offers).offer
|
||||
LET supplier_dist = DISTANCE(first_offer.latitude, first_offer.longitude, @lat, @lon) / 1000
|
||||
|
||||
SORT supplier_dist ASC
|
||||
LIMIT @limit
|
||||
RETURN {
|
||||
uuid: supplier_uuid,
|
||||
name: supplier_node != null ? supplier_node.name : first_offer.supplier_name,
|
||||
latitude: supplier_node != null ? supplier_node.latitude : first_offer.latitude,
|
||||
longitude: supplier_node != null ? supplier_node.longitude : first_offer.longitude,
|
||||
distance_km: supplier_dist
|
||||
}
|
||||
"""
|
||||
// Try to find supplier node for full info
|
||||
LET supplier_node = FIRST(
|
||||
FOR s IN nodes
|
||||
FILTER s._key == supplier_uuid
|
||||
FILTER s.node_type == 'supplier'
|
||||
RETURN s
|
||||
)
|
||||
|
||||
bind_vars = {'lat': lat, 'lon': lon, 'radius': radius, 'limit': limit}
|
||||
if product_uuid:
|
||||
bind_vars['product_uuid'] = product_uuid
|
||||
SORT supplier_dist ASC
|
||||
LIMIT @limit
|
||||
RETURN {
|
||||
uuid: supplier_uuid,
|
||||
name: supplier_node != null ? supplier_node.name : first_offer.supplier_name,
|
||||
latitude: supplier_node != null ? supplier_node.latitude : first_offer.latitude,
|
||||
longitude: supplier_node != null ? supplier_node.longitude : first_offer.longitude,
|
||||
distance_km: supplier_dist
|
||||
}
|
||||
"""
|
||||
bind_vars = {'lat': lat, 'lon': lon, 'limit': limit}
|
||||
|
||||
try:
|
||||
cursor = db.aql.execute(aql, bind_vars=bind_vars)
|
||||
@@ -1971,7 +1711,10 @@ class Query(graphene.ObjectType):
|
||||
longitude=s.get('longitude'),
|
||||
distance_km=s.get('distance_km'),
|
||||
))
|
||||
logger.info("Found %d suppliers near (%.3f, %.3f) within %d km", len(suppliers), lat, lon, radius)
|
||||
if product_uuid:
|
||||
logger.info("Found %d suppliers for product %s", len(suppliers), product_uuid)
|
||||
else:
|
||||
logger.info("Found %d suppliers near (%.3f, %.3f)", len(suppliers), lat, lon)
|
||||
return suppliers
|
||||
except Exception as e:
|
||||
logger.error("Error finding nearest suppliers: %s", e)
|
||||
|
||||
Reference in New Issue
Block a user