From 1162f53c2aed7875ee60ca74f403e678f704fa43 Mon Sep 17 00:00:00 2001 From: Itz-Agasta Date: Wed, 12 Aug 2026 13:40:06 +0530 Subject: [PATCH] Add include/exclude parameters to the search endpoint --- src/nominatim_api/v1/server_glue.py | 2 + test/bdd/features/api/search/params.feature | 50 +++++++++++++++++++++ test/python/api/test_server_glue_v1.py | 28 ++++++++++++ 3 files changed, 80 insertions(+) diff --git a/src/nominatim_api/v1/server_glue.py b/src/nominatim_api/v1/server_glue.py index f74e40ab..ffc7b6dc 100644 --- a/src/nominatim_api/v1/server_glue.py +++ b/src/nominatim_api/v1/server_glue.py @@ -346,6 +346,8 @@ async def search_endpoint(api: NominatimAPIAsync, params: ASGIAdaptor) -> Any: details['viewbox'] = params.get('viewbox', None) or params.get('viewboxlbrt', None) details['bounded_viewbox'] = params.get_bool('bounded', False) details['dedupe'] = params.get_bool('dedupe', True) + details['include'] = params.get_all('include') + details['exclude'] = params.get_all('exclude') max_results = max(1, min(50, params.get_int('limit', 10))) details['max_results'] = (max_results + min(10, max_results) diff --git a/test/bdd/features/api/search/params.feature b/test/bdd/features/api/search/params.feature index f00cbb19..98f1f312 100644 --- a/test/bdd/features/api/search/params.feature +++ b/test/bdd/features/api/search/params.feature @@ -407,3 +407,53 @@ Feature: Search queries | polygon_text | geotext | | polygon_svg | svg | | polygon_kml | geokml | + + Scenario: Restrict results to a category + When geocoding "Boccia Club" + | include | + | osm.leisure.sports_centre | + Then more than 0 results are returned + When geocoding "Boccia Club" + | include | + | osm.amenity.cafe | + Then exactly 0 results are returned + + Scenario: A category matches all its descendants + When geocoding "Boccia Club" + | include | + | osm.leisure | + Then more than 0 results are returned + When geocoding "Boccia Club" + | include | + | osm.tourism | + Then exactly 0 results are returned + + Scenario: A place matches any of its categories + When geocoding "Boccia Club" + | include | + | osm.building.yes | + Then more than 0 results are returned + + Scenario: Comma-separated categories match any of them + When geocoding "Boccia Club" + | include | + | osm.amenity.cafe,osm.leisure.sports_centre | + Then more than 0 results are returned + + Scenario: Exclude drops results of the given category + When geocoding "Boccia Club" + | exclude | + | osm.building.yes | + Then exactly 0 results are returned + + Scenario Outline: Invalid categories are rejected + When sending v1/search + | q | | + | Boccia Club | | + Then a HTTP 400 is returned + + Examples: + | param | value | + | include | osm | + | include | | + | exclude | osm.amenity;cafe | diff --git a/test/python/api/test_server_glue_v1.py b/test/python/api/test_server_glue_v1.py index 85fd7cdb..8314b4cc 100644 --- a/test/python/api/test_server_glue_v1.py +++ b/test/python/api/test_server_glue_v1.py @@ -601,6 +601,34 @@ class TestSearchEndPointSearch: with pytest.raises(FakeError, match='^400 -- .*cannot be used together'): await glue.search_endpoint(napi.NominatimAPIAsync(), a) + @pytest.mark.asyncio + @pytest.mark.parametrize('params,include,exclude', [ + ({}, [], []), + ({'include': 'osm.amenity.cafe'}, ['osm.amenity.cafe'], []), + ({'include': ['osm.tourism.hotel', 'osm.amenity.restaurant']}, + ['osm.tourism.hotel', 'osm.amenity.restaurant'], []), + ({'exclude': ['osm.amenity.fast_food']}, [], ['osm.amenity.fast_food']), + ({'include': 'osm.amenity', 'exclude': 'osm.amenity.fast_food'}, + ['osm.amenity'], ['osm.amenity.fast_food']), + ]) + async def test_search_category_filters(self, monkeypatch, params, include, exclude): + details = {} + + async def _search(self, query, **kwargs): + details.update(kwargs) + return napi.SearchResults() + + monkeypatch.setattr(napi.NominatimAPIAsync, 'search', _search) + + a = FakeAdaptor() + a.params['q'] = 'something' + a.params.update(params) + + await glue.search_endpoint(napi.NominatimAPIAsync(), a) + + assert details['include'] == include + assert details['exclude'] == exclude + @pytest.mark.asyncio @pytest.mark.parametrize('dedupe,numres', [(True, 1), (False, 2)]) async def test_search_dedupe(self, dedupe, numres): -- 2.47.3