Skip to content

Commit

Permalink
Merge pull request osm-search#3212 from lonvia/more-tests
Browse files Browse the repository at this point in the history
Add more unit tests for search
  • Loading branch information
lonvia authored Oct 5, 2023
2 parents f1fbcd8 + b00b16a commit 54cb9a3
Show file tree
Hide file tree
Showing 6 changed files with 298 additions and 14 deletions.
3 changes: 2 additions & 1 deletion nominatim/api/results.py
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,8 @@ def create_from_country_row(row: Optional[SaRow],
centroid=Point.from_wkb(row.centroid),
names=row.name,
rank_address=4, rank_search=4,
country_code=row.country_code)
country_code=row.country_code,
geometry=_filter_geometries(row))


async def add_result_details(conn: SearchConnection, results: List[BaseResultT],
Expand Down
9 changes: 7 additions & 2 deletions nominatim/api/search/db_searches.py
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,8 @@ async def lookup_in_country_table(self, conn: SearchConnection,

sql = sa.select(tgrid.c.country_code,
tgrid.c.geometry.ST_Centroid().ST_Collect().ST_Centroid()
.label('centroid'))\
.label('centroid'),
tgrid.c.geometry.ST_Collect().ST_Expand(0).label('bbox'))\
.where(tgrid.c.country_code.in_(self.countries.values))\
.group_by(tgrid.c.country_code)

Expand All @@ -465,13 +466,17 @@ async def lookup_in_country_table(self, conn: SearchConnection,
+ sa.func.coalesce(t.c.derived_name,
sa.cast('', type_=conn.t.types.Composite))
).label('name'),
sub.c.centroid)\
sub.c.centroid, sub.c.bbox)\
.join(sub, t.c.country_code == sub.c.country_code)

if details.geometry_output:
sql = _add_geometry_columns(sql, sub.c.centroid, details)

results = nres.SearchResults()
for row in await conn.execute(sql, _details_to_bind_params(details)):
result = nres.create_from_country_row(row, nres.SearchResult)
assert result
result.bbox = Bbox.from_wkb(row.bbox)
result.accuracy = self.penalty + self.countries.get_penalty(row.country_code, 5.0)
results.append(result)

Expand Down
67 changes: 67 additions & 0 deletions test/python/api/search/test_search_country.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,70 @@ def test_find_from_fallback_countries(apiobj):

def test_find_none(apiobj):
assert len(run_search(apiobj, 0.0, ['xx'])) == 0


@pytest.mark.parametrize('coord,numres', [((0.5, 1), 1), ((10, 10), 0)])
def test_find_near(apiobj, coord, numres):
apiobj.add_country('ro', 'POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))')
apiobj.add_country_name('ro', {'name': 'România'})

results = run_search(apiobj, 0.0, ['ro'],
details=SearchDetails(near=napi.Point(*coord),
near_radius=0.1))

assert len(results) == numres


class TestCountryParameters:

@pytest.fixture(autouse=True)
def fill_database(self, apiobj):
apiobj.add_placex(place_id=55, class_='boundary', type='administrative',
rank_search=4, rank_address=4,
name={'name': 'Lolaland'},
country_code='yw',
centroid=(10, 10),
geometry='POLYGON((9.5 9.5, 9.5 10.5, 10.5 10.5, 10.5 9.5, 9.5 9.5))')
apiobj.add_country('ro', 'POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))')
apiobj.add_country_name('ro', {'name': 'România'})


@pytest.mark.parametrize('geom', [napi.GeometryFormat.GEOJSON,
napi.GeometryFormat.KML,
napi.GeometryFormat.SVG,
napi.GeometryFormat.TEXT])
@pytest.mark.parametrize('cc', ['yw', 'ro'])
def test_return_geometries(self, apiobj, geom, cc):
results = run_search(apiobj, 0.5, [cc],
details=SearchDetails(geometry_output=geom))

assert len(results) == 1
assert geom.name.lower() in results[0].geometry


@pytest.mark.parametrize('pid,rids', [(76, [55]), (55, [])])
def test_exclude_place_id(self, apiobj, pid, rids):
results = run_search(apiobj, 0.5, ['yw', 'ro'],
details=SearchDetails(excluded=[pid]))

assert [r.place_id for r in results] == rids


@pytest.mark.parametrize('viewbox,rids', [((9, 9, 11, 11), [55]),
((-10, -10, -3, -3), [])])
def test_bounded_viewbox_in_placex(self, apiobj, viewbox, rids):
results = run_search(apiobj, 0.5, ['yw'],
details=SearchDetails.from_kwargs({'viewbox': viewbox,
'bounded_viewbox': True}))

assert [r.place_id for r in results] == rids


@pytest.mark.parametrize('viewbox,numres', [((0, 0, 1, 1), 1),
((-10, -10, -3, -3), 0)])
def test_bounded_viewbox_in_fallback(self, apiobj, viewbox, numres):
results = run_search(apiobj, 0.5, ['ro'],
details=SearchDetails.from_kwargs({'viewbox': viewbox,
'bounded_viewbox': True}))

assert len(results) == numres
67 changes: 65 additions & 2 deletions test/python/api/search/test_search_near.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,21 @@
FieldLookup, FieldRanking, RankedTokens


def run_search(apiobj, global_penalty, cat, cat_penalty=None,
def run_search(apiobj, global_penalty, cat, cat_penalty=None, ccodes=[],
details=SearchDetails()):

class PlaceSearchData:
penalty = 0.0
postcodes = WeightedStrings([], [])
countries = WeightedStrings([], [])
countries = WeightedStrings(ccodes, [0.0] * len(ccodes))
housenumbers = WeightedStrings([], [])
qualifiers = WeightedStrings([], [])
lookups = [FieldLookup('name_vector', [56], 'lookup_all')]
rankings = []

if ccodes is not None:
details.countries = ccodes

place_search = PlaceSearch(0.0, PlaceSearchData(), 2)

if cat_penalty is None:
Expand All @@ -49,6 +52,18 @@ def test_no_results_inner_query(apiobj):
assert not run_search(apiobj, 0.4, [('this', 'that')])


def test_no_appropriate_results_inner_query(apiobj):
apiobj.add_placex(place_id=100, country_code='us',
centroid=(5.6, 4.3),
geometry='POLYGON((0.0 0.0, 10.0 0.0, 10.0 2.0, 0.0 2.0, 0.0 0.0))')
apiobj.add_search_name(100, names=[56], country_code='us',
centroid=(5.6, 4.3))
apiobj.add_placex(place_id=22, class_='amenity', type='bank',
centroid=(5.6001, 4.2994))

assert not run_search(apiobj, 0.4, [('amenity', 'bank')])


class TestNearSearch:

@pytest.fixture(autouse=True)
Expand Down Expand Up @@ -100,3 +115,51 @@ def test_near_in_classtype(self, apiobj):

assert [r.place_id for r in results] == [22]


@pytest.mark.parametrize('cc,rid', [('us', 22), ('mx', 23)])
def test_restrict_by_country(self, apiobj, cc, rid):
apiobj.add_placex(place_id=22, class_='amenity', type='bank',
centroid=(5.6001, 4.2994),
country_code='us')
apiobj.add_placex(place_id=122, class_='amenity', type='bank',
centroid=(5.6001, 4.2994),
country_code='mx')
apiobj.add_placex(place_id=23, class_='amenity', type='bank',
centroid=(-10.3001, 56.9),
country_code='mx')
apiobj.add_placex(place_id=123, class_='amenity', type='bank',
centroid=(-10.3001, 56.9),
country_code='us')

results = run_search(apiobj, 0.1, [('amenity', 'bank')], ccodes=[cc, 'fr'])

assert [r.place_id for r in results] == [rid]


@pytest.mark.parametrize('excluded,rid', [(22, 122), (122, 22)])
def test_exclude_place_by_id(self, apiobj, excluded, rid):
apiobj.add_placex(place_id=22, class_='amenity', type='bank',
centroid=(5.6001, 4.2994),
country_code='us')
apiobj.add_placex(place_id=122, class_='amenity', type='bank',
centroid=(5.6001, 4.2994),
country_code='us')


results = run_search(apiobj, 0.1, [('amenity', 'bank')],
details=SearchDetails(excluded=[excluded]))

assert [r.place_id for r in results] == [rid]


@pytest.mark.parametrize('layer,rids', [(napi.DataLayer.POI, [22]),
(napi.DataLayer.MANMADE, [])])
def test_with_layer(self, apiobj, layer, rids):
apiobj.add_placex(place_id=22, class_='amenity', type='bank',
centroid=(5.6001, 4.2994),
country_code='us')

results = run_search(apiobj, 0.1, [('amenity', 'bank')],
details=SearchDetails(layers=layer))

assert [r.place_id for r in results] == rids
99 changes: 92 additions & 7 deletions test/python/api/search/test_search_places.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
"""
Tests for running the generic place searcher.
"""
import json

import pytest

import nominatim.api as napi
Expand Down Expand Up @@ -130,23 +132,48 @@ def test_return_geometries(self, apiobj, geom):
assert geom.name.lower() in results[0].geometry


@pytest.mark.parametrize('factor,npoints', [(0.0, 3), (1.0, 2)])
def test_return_simplified_geometry(self, apiobj, factor, npoints):
apiobj.add_placex(place_id=333, country_code='us',
centroid=(9.0, 9.0),
geometry='LINESTRING(8.9 9.0, 9.0 9.0, 9.1 9.0)')
apiobj.add_search_name(333, names=[55], country_code='us',
centroid=(5.6, 4.3))

lookup = FieldLookup('name_vector', [55], 'lookup_all')
ranking = FieldRanking('name_vector', 0.9, [RankedTokens(0.0, [21])])

results = run_search(apiobj, 0.1, [lookup], [ranking],
details=SearchDetails(geometry_output=napi.GeometryFormat.GEOJSON,
geometry_simplification=factor))

assert len(results) == 1
result = results[0]
geom = json.loads(result.geometry['geojson'])

assert result.place_id == 333
assert len(geom['coordinates']) == npoints


@pytest.mark.parametrize('viewbox', ['5.0,4.0,6.0,5.0', '5.7,4.0,6.0,5.0'])
def test_prefer_viewbox(self, apiobj, viewbox):
@pytest.mark.parametrize('wcount,rids', [(2, [100, 101]), (20000, [100])])
def test_prefer_viewbox(self, apiobj, viewbox, wcount, rids):
lookup = FieldLookup('name_vector', [1, 2], 'lookup_all')
ranking = FieldRanking('name_vector', 0.9, [RankedTokens(0.0, [21])])

results = run_search(apiobj, 0.1, [lookup], [ranking])
assert [r.place_id for r in results] == [101, 100]

results = run_search(apiobj, 0.1, [lookup], [ranking],
results = run_search(apiobj, 0.1, [lookup], [ranking], count=wcount,
details=SearchDetails.from_kwargs({'viewbox': viewbox}))
assert [r.place_id for r in results] == [100, 101]
assert [r.place_id for r in results] == rids


def test_force_viewbox(self, apiobj):
@pytest.mark.parametrize('viewbox', ['5.0,4.0,6.0,5.0', '5.55,4.27,5.62,4.31'])
def test_force_viewbox(self, apiobj, viewbox):
lookup = FieldLookup('name_vector', [1, 2], 'lookup_all')

details=SearchDetails.from_kwargs({'viewbox': '5.0,4.0,6.0,5.0',
details=SearchDetails.from_kwargs({'viewbox': viewbox,
'bounded_viewbox': True})

results = run_search(apiobj, 0.1, [lookup], [], details=details)
Expand All @@ -166,11 +193,12 @@ def test_prefer_near(self, apiobj):
assert [r.place_id for r in results] == [100, 101]


def test_force_near(self, apiobj):
@pytest.mark.parametrize('radius', [0.09, 0.11])
def test_force_near(self, apiobj, radius):
lookup = FieldLookup('name_vector', [1, 2], 'lookup_all')

details=SearchDetails.from_kwargs({'near': '5.6,4.3',
'near_radius': 0.11})
'near_radius': radius})

results = run_search(apiobj, 0.1, [lookup], [], details=details)

Expand Down Expand Up @@ -287,6 +315,34 @@ def test_very_large_housenumber(apiobj):
assert [r.place_id for r in results] == [93, 2000]


@pytest.mark.parametrize('wcount,rids', [(2, [990, 991]), (30000, [990])])
def test_name_and_postcode(apiobj, wcount, rids):
apiobj.add_placex(place_id=990, class_='highway', type='service',
rank_search=27, rank_address=27,
postcode='11225',
centroid=(10.0, 10.0),
geometry='LINESTRING(9.995 10, 10.005 10)')
apiobj.add_search_name(990, names=[111], centroid=(10.0, 10.0),
search_rank=27, address_rank=27)
apiobj.add_placex(place_id=991, class_='highway', type='service',
rank_search=27, rank_address=27,
postcode='11221',
centroid=(10.1, 10.1),
geometry='LINESTRING(9.995 10.1, 10.005 10.1)')
apiobj.add_search_name(991, names=[111], centroid=(10.1, 10.1),
search_rank=27, address_rank=27)
apiobj.add_postcode(place_id=100, country_code='ch', postcode='11225',
geometry='POINT(10 10)')

lookup = FieldLookup('name_vector', [111], 'lookup_all')

results = run_search(apiobj, 0.1, [lookup], [], pcs=['11225'], count=wcount,
details=SearchDetails())

assert results
assert [r.place_id for r in results] == rids


class TestInterpolations:

@pytest.fixture(autouse=True)
Expand Down Expand Up @@ -318,6 +374,21 @@ def test_lookup_housenumber(self, apiobj, hnr, res):
assert [r.place_id for r in results] == res + [990]


@pytest.mark.parametrize('geom', [napi.GeometryFormat.GEOJSON,
napi.GeometryFormat.KML,
napi.GeometryFormat.SVG,
napi.GeometryFormat.TEXT])
def test_osmline_with_geometries(self, apiobj, geom):
lookup = FieldLookup('name_vector', [111], 'lookup_all')

results = run_search(apiobj, 0.1, [lookup], [], hnrs=['21'],
details=SearchDetails(geometry_output=geom))

assert results[0].place_id == 992
assert geom.name.lower() in results[0].geometry



class TestTiger:

@pytest.fixture(autouse=True)
Expand Down Expand Up @@ -351,6 +422,20 @@ def test_lookup_housenumber(self, apiobj, hnr, res):
assert [r.place_id for r in results] == res + [990]


@pytest.mark.parametrize('geom', [napi.GeometryFormat.GEOJSON,
napi.GeometryFormat.KML,
napi.GeometryFormat.SVG,
napi.GeometryFormat.TEXT])
def test_tiger_with_geometries(self, apiobj, geom):
lookup = FieldLookup('name_vector', [111], 'lookup_all')

results = run_search(apiobj, 0.1, [lookup], [], hnrs=['21'],
details=SearchDetails(geometry_output=geom))

assert results[0].place_id == 992
assert geom.name.lower() in results[0].geometry


class TestLayersRank30:

@pytest.fixture(autouse=True)
Expand Down
Loading

0 comments on commit 54cb9a3

Please sign in to comment.