]> git.openstreetmap.org Git - nominatim.git/blob - test/python/api/search/test_api_search_query.py
Merge remote-tracking branch 'upstream/master'
[nominatim.git] / test / python / api / search / test_api_search_query.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) 2025 by the Nominatim developer community.
6 # For a full list of authors see the git log.
7 """
8 Tests for tokenized query data structures.
9 """
10 import pytest
11
12 from nominatim_api.search import query
13
14
15 class MyToken(query.Token):
16
17     def get_category(self):
18         return 'this', 'that'
19
20
21 def mktoken(tid: int):
22     return MyToken(penalty=3.0, token=tid, count=1, addr_count=1,
23                    lookup_word='foo')
24
25
26 @pytest.fixture
27 def qnode():
28     return query.QueryNode(query.BREAK_PHRASE, query.PHRASE_ANY, 0.0, '', '')
29
30
31 @pytest.mark.parametrize('ptype,ttype', [(query.PHRASE_ANY, 'W'),
32                                          (query.PHRASE_AMENITY, 'Q'),
33                                          (query.PHRASE_STREET, 'w'),
34                                          (query.PHRASE_CITY, 'W'),
35                                          (query.PHRASE_COUNTRY, 'C'),
36                                          (query.PHRASE_POSTCODE, 'P')])
37 def test_phrase_compatible(ptype, ttype):
38     assert query._phrase_compatible_with(ptype, ttype, False)
39
40
41 @pytest.mark.parametrize('ptype', [query.PHRASE_COUNTRY, query.PHRASE_POSTCODE])
42 def test_phrase_incompatible(ptype):
43     assert not query._phrase_compatible_with(ptype, query.TOKEN_PARTIAL, True)
44
45
46 def test_query_node_empty(qnode):
47     assert not qnode.has_tokens(3, query.TOKEN_PARTIAL)
48     assert qnode.get_tokens(3, query.TOKEN_WORD) is None
49
50
51 def test_query_node_with_content(qnode):
52     qnode.starting.append(query.TokenList(2, query.TOKEN_PARTIAL, [mktoken(100), mktoken(101)]))
53     qnode.starting.append(query.TokenList(2, query.TOKEN_WORD, [mktoken(1000)]))
54
55     assert not qnode.has_tokens(3, query.TOKEN_PARTIAL)
56     assert not qnode.has_tokens(2, query.TOKEN_COUNTRY)
57     assert qnode.has_tokens(2, query.TOKEN_PARTIAL)
58     assert qnode.has_tokens(2, query.TOKEN_WORD)
59
60     assert qnode.get_tokens(3, query.TOKEN_PARTIAL) is None
61     assert qnode.get_tokens(2, query.TOKEN_COUNTRY) is None
62     assert len(qnode.get_tokens(2, query.TOKEN_PARTIAL)) == 2
63     assert len(qnode.get_tokens(2, query.TOKEN_WORD)) == 1
64
65
66 def test_query_struct_empty():
67     q = query.QueryStruct([])
68
69     assert q.num_token_slots() == 0
70
71
72 def test_query_struct_with_tokens():
73     q = query.QueryStruct([query.Phrase(query.PHRASE_ANY, 'foo bar')])
74     q.add_node(query.BREAK_WORD, query.PHRASE_ANY)
75     q.add_node(query.BREAK_END, query.PHRASE_ANY)
76
77     assert q.num_token_slots() == 2
78
79     q.add_token(query.TokenRange(0, 1), query.TOKEN_PARTIAL, mktoken(1))
80     q.add_token(query.TokenRange(1, 2), query.TOKEN_PARTIAL, mktoken(2))
81     q.add_token(query.TokenRange(1, 2), query.TOKEN_WORD, mktoken(99))
82     q.add_token(query.TokenRange(1, 2), query.TOKEN_WORD, mktoken(98))
83
84     assert q.get_tokens(query.TokenRange(0, 2), query.TOKEN_WORD) == []
85     assert len(q.get_tokens(query.TokenRange(1, 2), query.TOKEN_WORD)) == 2
86
87     partials = q.get_partials_list(query.TokenRange(0, 2))
88
89     assert len(partials) == 2
90     assert [t.token for t in partials] == [1, 2]
91
92     assert q.find_lookup_word_by_id(4) == 'None'
93     assert q.find_lookup_word_by_id(99) == '[W]foo'
94
95
96 def test_query_struct_incompatible_token():
97     q = query.QueryStruct([query.Phrase(query.PHRASE_COUNTRY, 'foo bar')])
98     q.add_node(query.BREAK_WORD, query.PHRASE_COUNTRY)
99     q.add_node(query.BREAK_END, query.PHRASE_ANY)
100
101     q.add_token(query.TokenRange(0, 1), query.TOKEN_PARTIAL, mktoken(1))
102     q.add_token(query.TokenRange(1, 2), query.TOKEN_COUNTRY, mktoken(100))
103
104     assert q.get_tokens(query.TokenRange(0, 1), query.TOKEN_PARTIAL) == []
105     assert len(q.get_tokens(query.TokenRange(1, 2), query.TOKEN_COUNTRY)) == 1
106
107
108 def test_query_struct_amenity_single_word():
109     q = query.QueryStruct([query.Phrase(query.PHRASE_AMENITY, 'bar')])
110     q.add_node(query.BREAK_END, query.PHRASE_ANY)
111
112     q.add_token(query.TokenRange(0, 1), query.TOKEN_PARTIAL, mktoken(1))
113     q.add_token(query.TokenRange(0, 1), query.TOKEN_NEAR_ITEM, mktoken(2))
114     q.add_token(query.TokenRange(0, 1), query.TOKEN_QUALIFIER, mktoken(3))
115
116     assert len(q.get_tokens(query.TokenRange(0, 1), query.TOKEN_PARTIAL)) == 1
117     assert len(q.get_tokens(query.TokenRange(0, 1), query.TOKEN_NEAR_ITEM)) == 1
118     assert len(q.get_tokens(query.TokenRange(0, 1), query.TOKEN_QUALIFIER)) == 0
119
120
121 def test_query_struct_amenity_two_words():
122     q = query.QueryStruct([query.Phrase(query.PHRASE_AMENITY, 'foo bar')])
123     q.add_node(query.BREAK_WORD, query.PHRASE_AMENITY)
124     q.add_node(query.BREAK_END, query.PHRASE_ANY)
125
126     for trange in [(0, 1), (1, 2)]:
127         q.add_token(query.TokenRange(*trange), query.TOKEN_PARTIAL, mktoken(1))
128         q.add_token(query.TokenRange(*trange), query.TOKEN_NEAR_ITEM, mktoken(2))
129         q.add_token(query.TokenRange(*trange), query.TOKEN_QUALIFIER, mktoken(3))
130
131     assert len(q.get_tokens(query.TokenRange(0, 1), query.TOKEN_PARTIAL)) == 1
132     assert len(q.get_tokens(query.TokenRange(0, 1), query.TOKEN_NEAR_ITEM)) == 0
133     assert len(q.get_tokens(query.TokenRange(0, 1), query.TOKEN_QUALIFIER)) == 1
134
135     assert len(q.get_tokens(query.TokenRange(1, 2), query.TOKEN_PARTIAL)) == 1
136     assert len(q.get_tokens(query.TokenRange(1, 2), query.TOKEN_NEAR_ITEM)) == 0
137     assert len(q.get_tokens(query.TokenRange(1, 2), query.TOKEN_QUALIFIER)) == 1