From 27b05cf36266ef7a54b955bbef61940947bc96a9 Mon Sep 17 00:00:00 2001 From: Ruslan Bakiev <572431+veikab@users.noreply.github.com> Date: Sat, 24 Jan 2026 12:13:29 +0700 Subject: [PATCH] Add bounds filtering to nodes query for map-based selection --- geo_app/schema.py | 54 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 48 insertions(+), 6 deletions(-) diff --git a/geo_app/schema.py b/geo_app/schema.py index dfb2e2e..0b898eb 100644 --- a/geo_app/schema.py +++ b/geo_app/schema.py @@ -153,11 +153,19 @@ class Query(graphene.ObjectType): transport_type=graphene.String(), country=graphene.String(description="Filter by country name"), search=graphene.String(description="Search by node name (case-insensitive)"), + west=graphene.Float(description="Bounding box west longitude"), + south=graphene.Float(description="Bounding box south latitude"), + east=graphene.Float(description="Bounding box east longitude"), + north=graphene.Float(description="Bounding box north latitude"), ) nodes_count = graphene.Int( transport_type=graphene.String(), country=graphene.String(description="Filter by country name"), - description="Get total count of nodes (with optional transport/country filter)", + west=graphene.Float(description="Bounding box west longitude"), + south=graphene.Float(description="Bounding box south latitude"), + east=graphene.Float(description="Bounding box east longitude"), + north=graphene.Float(description="Bounding box north latitude"), + description="Get total count of nodes (with optional transport/country/bounds filter)", ) hub_countries = graphene.List( @@ -402,18 +410,29 @@ class Query(graphene.ObjectType): edges=[EdgeType(**e) for e in edges], ) - def resolve_nodes(self, info, limit=None, offset=None, transport_type=None, country=None, search=None): + def resolve_nodes(self, info, limit=None, offset=None, transport_type=None, country=None, search=None, + west=None, south=None, east=None, north=None): """Get all logistics nodes (without edges for list view).""" db = get_db() + # Build bounds filter if all bounds are provided + bounds_filter = "" + if west is not None and south is not None and east is not None and north is not None: + bounds_filter = """ + FILTER node.latitude != null AND node.longitude != null + FILTER node.latitude >= @south AND node.latitude <= @north + FILTER node.longitude >= @west AND node.longitude <= @east + """ + # Only return logistics nodes (not buyer/seller addresses) - aql = """ + aql = f""" FOR node IN nodes FILTER node.node_type == 'logistics' OR node.node_type == null LET types = node.transport_types != null ? node.transport_types : [] FILTER @transport_type == null OR @transport_type IN types FILTER @country == null OR node.country == @country FILTER @search == null OR CONTAINS(LOWER(node.name), LOWER(@search)) OR CONTAINS(LOWER(node.country), LOWER(@search)) + {bounds_filter} SORT node.name ASC LIMIT @offset, @limit RETURN node @@ -426,6 +445,10 @@ class Query(graphene.ObjectType): 'search': search, 'offset': 0 if offset is None else offset, 'limit': 1000000 if limit is None else limit, + 'west': west, + 'south': south, + 'east': east, + 'north': north, }, ) @@ -446,18 +469,37 @@ class Query(graphene.ObjectType): logger.info("Returning %d nodes", len(nodes)) return nodes - def resolve_nodes_count(self, info, transport_type=None, country=None): + def resolve_nodes_count(self, info, transport_type=None, country=None, + west=None, south=None, east=None, north=None): db = get_db() - aql = """ + + # Build bounds filter if all bounds are provided + bounds_filter = "" + if west is not None and south is not None and east is not None and north is not None: + bounds_filter = """ + FILTER node.latitude != null AND node.longitude != null + FILTER node.latitude >= @south AND node.latitude <= @north + FILTER node.longitude >= @west AND node.longitude <= @east + """ + + aql = f""" FOR node IN nodes FILTER node.node_type == 'logistics' OR node.node_type == null LET types = node.transport_types != null ? node.transport_types : [] FILTER @transport_type == null OR @transport_type IN types FILTER @country == null OR node.country == @country + {bounds_filter} COLLECT WITH COUNT INTO length RETURN length """ - cursor = db.aql.execute(aql, bind_vars={'transport_type': transport_type, 'country': country}) + cursor = db.aql.execute(aql, bind_vars={ + 'transport_type': transport_type, + 'country': country, + 'west': west, + 'south': south, + 'east': east, + 'north': north, + }) return next(cursor, 0) def resolve_hub_countries(self, info):