]> git.openstreetmap.org Git - nominatim.git/blob - test/python/tokenizer/sanitizers/test_clean_multilingual_names.py
release 5.3.2.post9
[nominatim.git] / test / python / tokenizer / sanitizers / test_clean_multilingual_names.py
1 # SPDX-License-Identifier: GPL-3.0-or-later
2 #
3 # This file is part of Nominatim. (https://nominatim.org)
4 #
5 # Copyright (C) 2026 by the Nominatim developer community.
6 # For a full list of authors see the git log.
7 """
8 Tests for the sanitizer that removes multilingual concatenated names.
9 """
10 import pytest
11
12 from nominatim_db.config import Configuration
13 from nominatim_db.data.place_info import PlaceInfo
14 from nominatim_db.tokenizer.place_sanitizer import PlaceSanitizer
15
16
17 @pytest.fixture
18 def run_sanitizer(def_config):
19     def _run(extra_args=None, **kwargs):
20         place = PlaceInfo({'name': {k.replace('_', ':'): v
21                                     for k, v in kwargs.items()},
22                            'country_code': 'be', 'rank_address': 26})
23
24         sanitizer_args = {'step': 'clean-multilingual-names'}
25         if extra_args:
26             sanitizer_args.update(extra_args)
27
28         PlaceSanitizer([sanitizer_args], def_config).process_names(place)
29
30         return sorted([(p.name, p.kind, p.suffix or '')
31                        for p in place.searchable_names])
32
33     return _run
34
35
36 class TestDelimiter:
37     """ Test removal with some common delimiters used worlwide.
38     """
39
40     def test_hyphen_delimiter(self, run_sanitizer):
41         res = run_sanitizer(name='गढ़वा - Garhwa - گڑھوا',
42                             name_hi='गढ़वा',
43                             name_en='Garhwa',
44                             name_ur='گڑھوا')
45
46         assert ('गढ़वा - Garhwa - گڑھوا', 'name', '') not in res
47         assert ('गढ़वा', 'name', 'hi') in res
48         assert ('Garhwa', 'name', 'en') in res
49         assert ('گڑھوا', 'name', 'ur') in res
50
51     def test_slash_delimiter(self, run_sanitizer):
52         res = run_sanitizer(name='Rue de la Gare / Stationsstraat/Bahnhofstraße',
53                             name_fr='Rue de la Gare',
54                             name_nl='Stationsstraat',
55                             name_de='Bahnhofstraße')
56
57         assert ('Rue de la Gare / Stationsstraat/Bahnhofstraße', 'name', '') not in res
58         assert ('Rue de la Gare', 'name', 'fr') in res
59         assert ('Stationsstraat', 'name', 'nl') in res
60         assert ('Bahnhofstraße', 'name', 'de') in res
61
62     def test_space_delimiter(self, run_sanitizer):
63         res = run_sanitizer(name='Oran ⵡⴻⵀⵔⴰⵏ وهران',
64                             name_en='Oran',
65                             name_ber='ⵡⴻⵀⵔⴰⵏ',
66                             name_ar='وهران',
67                             name_be='Аран')
68
69         assert ('Oran ⵡⴻⵀⵔⴰⵏ وهران', 'name', '') not in res
70         assert ('Oran', 'name', 'en') in res
71         assert ('ⵡⴻⵀⵔⴰⵏ', 'name', 'ber') in res
72         assert ('وهران', 'name', 'ar') in res
73         assert ('Аран', 'name', 'be') in res
74
75     def test_space_delimiter_multiword_name(self, run_sanitizer):
76         res = run_sanitizer(name='Baarin Left Banner ᠪᠠᠭᠠᠷᠢᠨ ᠵᠡᠭᠦᠨ 巴林左旗',
77                             name_en='Baarin Left Banner',
78                             name_mn='ᠪᠠᠭᠠᠷᠢᠨ ᠵᠡᠭᠦᠨ',
79                             name_zh='巴林左旗')
80
81         assert ('Baarin Left Banner ᠪᠠᠭᠠᠷᠢᠨ ᠵᠡᠭᠦᠨ 巴林左旗', 'name', '') not in res
82         assert ('Baarin Left Banner', 'name', 'en') in res
83         assert ('ᠪᠠᠭᠠᠷᠢᠨ ᠵᠡᠭᠦᠨ', 'name', 'mn') in res
84         assert ('巴林左旗', 'name', 'zh') in res
85
86     def test_mixed_delimiters(self, run_sanitizer):
87         res = run_sanitizer(name='गढ़वा /Garhwa - گڑھوا',
88                             name_hi='गढ़वा',
89                             name_en='Garhwa',
90                             name_ur='گڑھوا')
91
92         assert ('गढ़वा /Garhwa - گڑھوا', 'name', '') not in res
93
94     def test_name_equals_single_lang_name(self, run_sanitizer):
95         res = run_sanitizer(name='New York City',
96                             name_en='New York City',
97                             name_fr='Ville de New York')
98
99         assert ('New York City', 'name', '') not in res
100
101     def test_name_does_not_match_any_pattern(self, run_sanitizer):
102         res = run_sanitizer(name='Some Unique Name',
103                             name_fr='Rue du Poulet',
104                             name_nl='Kiekenstraat')
105
106         assert ('Some Unique Name', 'name', '') in res
107
108     def test_partial_match(self, run_sanitizer):
109         res = run_sanitizer(name='Rue du Poulet - Unknown',
110                             name_fr='Rue du Poulet',
111                             name_nl='Kiekenstraat')
112
113         assert ('Rue du Poulet - Unknown', 'name', '') in res
114
115     def test_custom_delimiter(self, run_sanitizer):
116         res = run_sanitizer(extra_args={'delimiters': ['|']},
117                             name='गढ़वा|Garhwa | گڑھوا',
118                             name_hi='गढ़वा',
119                             name_en='Garhwa',
120                             name_ur='گڑھوا')
121
122         assert ('गढ़वा|Garhwa | گڑھوا', 'name', '') not in res
123
124     def test_delimeter_in_name(self, run_sanitizer):
125         res = run_sanitizer(name='Berchem-Sainte-Agathe - Sint-Agatha-Berchem',
126                             name_ar='بيرشيم سانت أغاث',
127                             name_fr='Berchem-Sainte-Agathe',
128                             name_la='Berchemium Agathae',
129                             name_li='Sint-Agatha-Berchem',
130                             name_nl='Sint-Agatha-Berchem',
131                             name_ru='Беркем-Сент-Агат')
132
133         assert ('Berchem-Sainte-Agathe - Sint-Agatha-Berchem', 'name', '') not in res
134         assert ('بيرشيم سانت أغاث', 'name', 'ar') in res
135         assert ('Berchem-Sainte-Agathe', 'name', 'fr') in res
136         assert ('Berchemium Agathae', 'name', 'la') in res
137         assert ('Sint-Agatha-Berchem', 'name', 'li') in res
138         assert ('Sint-Agatha-Berchem', 'name', 'nl') in res
139         assert ('Беркем-Сент-Агат', 'name', 'ru') in res
140
141     def test_leading_or_trailing_delimiter(self, run_sanitizer):
142         res = run_sanitizer(name='/ Foo - Bar - ',
143                             name_fr='Foo',
144                             name_es='Bar')
145
146         assert ('/ Foo - Bar -', 'name', '') not in res
147         assert ('Foo', 'name', 'fr') in res
148         assert ('Bar', 'name', 'es') in res
149
150     def test_longest_match_priority(self, run_sanitizer):
151         """ Test that overlapping substrings in language names do not break
152             the concatenation check due to premature regex matching.
153         """
154         res = run_sanitizer(name='New - New York',
155                             name_fr='New',
156                             name_en='New York')
157
158         assert ('New - New York', 'name', '') not in res
159         assert ('New', 'name', 'fr') in res
160         assert ('New York', 'name', 'en') in res
161
162     def test_non_language_suffix_ignored(self, run_sanitizer):
163         res = run_sanitizer(name='Old Town Hall',
164                             name_prefix='Old Town',
165                             name_suffix='Hall')
166
167         assert ('Old Town Hall', 'name', '') in res
168
169     def test_three_letter_language_used(self, run_sanitizer):
170         # A language code followed by a script variant is a valid suffix.
171         res = run_sanitizer(name='臺北 - Taipei',
172                             name_zh_Hant='臺北',
173                             name_en='Taipei')
174
175         assert ('臺北 - Taipei', 'name', '') not in res
176
177
178 class TestFilterKind:
179     """ Test with custom filter-kind configuration.
180     """
181
182     def test_filter_kind_ref(self, run_sanitizer):
183         place = PlaceInfo({'name': {'name': 'गढ़वा - Garhwa',
184                                     'name:fr': 'गढ़वा',
185                                     'name:nl': 'Garhwa',
186                                     'ref': 'A - B',
187                                     'ref:fr': 'A',
188                                     'ref:nl': 'B'},
189                            'country_code': 'be', 'rank_address': 26})
190
191         # When filter-kind is set to 'ref', only ref names are processed.
192         cfg = Configuration(None)
193         PlaceSanitizer([{'step': 'clean-multilingual-names',
194                          'filter-kind': 'ref'}], cfg).process_names(place)
195
196         names = [(p.name, p.kind, p.suffix or '') for p in place.searchable_names]
197         # 'name' bare tag should be kept (not matched by filter-kind)
198         assert ('गढ़वा - Garhwa', 'name', '') in names
199         # 'ref' bare tag should be removed
200         assert ('A - B', 'ref', '') not in names
201
202     def test_alt_name_not_affected_by_default(self, run_sanitizer):
203         place = PlaceInfo({'name': {'name': 'गढ़वा - Garhwa',
204                                     'name:fr': 'गढ़वा',
205                                     'name:nl': 'Garhwa',
206                                     'alt_name': 'Baz - Qux',
207                                     'alt_name:fr': 'Baz',
208                                     'alt_name:nl': 'Qux'},
209                            'country_code': 'be', 'rank_address': 26})
210
211         cfg = Configuration(None)
212         PlaceSanitizer([{'step': 'clean-multilingual-names'}], cfg).process_names(place)
213
214         names = [(p.name, p.kind, p.suffix or '') for p in place.searchable_names]
215         assert ('गढ़वा - Garhwa', 'name', '') not in names
216         assert ('गढ़वा', 'name', 'fr') in names
217         assert ('Garhwa', 'name', 'nl') in names
218         # alt_name kind, doesn't match default filter-kind='name'.
219         assert ('Baz - Qux', 'alt_name', '') in names
220         assert ('Baz', 'alt_name', 'fr') in names
221         assert ('Qux', 'alt_name', 'nl') in names