feat(schema): add bounds filtering to list endpoints
All checks were successful
Build Docker Image / build (push) Successful in 1m16s

Add west, south, east, north params to:
- hubs_list
- suppliers_list
- products_list

This enables filtering by map viewport bounds for the catalog.
This commit is contained in:
Ruslan Bakiev
2026-01-26 21:35:20 +07:00
parent ca01a91019
commit 9db56c5edc

View File

@@ -365,6 +365,10 @@ class Query(graphene.ObjectType):
offset=graphene.Int(default_value=0, description="Offset for pagination"),
country=graphene.String(description="Filter by country name"),
transport_type=graphene.String(description="Filter by transport type"),
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 paginated list of logistics hubs",
)
@@ -373,6 +377,10 @@ class Query(graphene.ObjectType):
limit=graphene.Int(default_value=50, description="Max results"),
offset=graphene.Int(default_value=0, description="Offset for pagination"),
country=graphene.String(description="Filter by country name"),
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 paginated list of suppliers from graph",
)
@@ -380,6 +388,10 @@ class Query(graphene.ObjectType):
ProductType,
limit=graphene.Int(default_value=50, description="Max results"),
offset=graphene.Int(default_value=0, description="Offset for pagination"),
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 paginated list of products from graph",
)
@@ -1683,30 +1695,52 @@ class Query(graphene.ObjectType):
logger.error("Error finding route to coordinates: %s", e)
return None
def resolve_hubs_list(self, info, limit=50, offset=0, country=None, transport_type=None):
def resolve_hubs_list(self, info, limit=50, offset=0, country=None, transport_type=None,
west=None, south=None, east=None, north=None):
"""Get paginated list of logistics hubs."""
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
FILTER node.product_uuid == 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}
SORT node.name ASC
LIMIT @offset, @limit
RETURN node
"""
try:
cursor = db.aql.execute(aql, bind_vars={
'transport_type': transport_type,
'country': country,
'offset': offset,
'limit': limit,
bind_vars = {
'transport_type': transport_type,
'country': country,
'offset': offset,
'limit': limit,
}
# Only add bounds to bind_vars if they are used
if west is not None and south is not None and east is not None and north is not None:
bind_vars.update({
'west': west,
'south': south,
'east': east,
'north': north,
})
try:
cursor = db.aql.execute(aql, bind_vars=bind_vars)
hubs = []
for node in cursor:
hubs.append(NodeType(
@@ -1726,26 +1760,48 @@ class Query(graphene.ObjectType):
logger.error("Error getting hubs list: %s", e)
return []
def resolve_suppliers_list(self, info, limit=50, offset=0, country=None):
def resolve_suppliers_list(self, info, limit=50, offset=0, country=None,
west=None, south=None, east=None, north=None):
"""Get paginated list of suppliers from graph."""
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 == 'supplier'
FILTER @country == null OR node.country == @country
{bounds_filter}
SORT node.name ASC
LIMIT @offset, @limit
RETURN node
"""
try:
cursor = db.aql.execute(aql, bind_vars={
'country': country,
'offset': offset,
'limit': limit,
bind_vars = {
'country': country,
'offset': offset,
'limit': limit,
}
# Only add bounds to bind_vars if they are used
if west is not None and south is not None and east is not None and north is not None:
bind_vars.update({
'west': west,
'south': south,
'east': east,
'north': north,
})
try:
cursor = db.aql.execute(aql, bind_vars=bind_vars)
suppliers = []
for node in cursor:
suppliers.append(SupplierType(
@@ -1760,30 +1816,52 @@ class Query(graphene.ObjectType):
logger.error("Error getting suppliers list: %s", e)
return []
def resolve_products_list(self, info, limit=50, offset=0):
def resolve_products_list(self, info, limit=50, offset=0,
west=None, south=None, east=None, north=None):
"""Get paginated list of products from graph."""
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 == 'offer'
FILTER node.product_uuid != null
{bounds_filter}
COLLECT product_uuid = node.product_uuid INTO offers
LET first_offer = FIRST(offers).node
SORT first_offer.product_name ASC
LIMIT @offset, @limit
RETURN {
RETURN {{
uuid: product_uuid,
name: first_offer.product_name
}
}}
"""
try:
cursor = db.aql.execute(aql, bind_vars={
'offset': offset,
'limit': limit,
bind_vars = {
'offset': offset,
'limit': limit,
}
# Only add bounds to bind_vars if they are used
if west is not None and south is not None and east is not None and north is not None:
bind_vars.update({
'west': west,
'south': south,
'east': east,
'north': north,
})
try:
cursor = db.aql.execute(aql, bind_vars=bind_vars)
products = [ProductType(uuid=p['uuid'], name=p.get('name')) for p in cursor]
logger.info("Returning %d products (offset=%d, limit=%d)", len(products), offset, limit)
return products