]> git.openstreetmap.org Git - nominatim.git/blob - test/python/test_indexing.py
introduce custom UsageError
[nominatim.git] / test / python / test_indexing.py
1 """
2 Tests for running the indexing.
3 """
4 import itertools
5 import psycopg2
6 import pytest
7
8 from nominatim.indexer.indexer import Indexer
9
10 class IndexerTestDB:
11
12     def __init__(self, conn):
13         self.placex_id = itertools.count(100000)
14         self.osmline_id = itertools.count(500000)
15
16         self.conn = conn
17         self.conn.set_isolation_level(0)
18         with self.conn.cursor() as cur:
19             cur.execute("""CREATE TABLE placex (place_id BIGINT,
20                                                 class TEXT,
21                                                 type TEXT,
22                                                 rank_address SMALLINT,
23                                                 rank_search SMALLINT,
24                                                 indexed_status SMALLINT,
25                                                 indexed_date TIMESTAMP,
26                                                 partition SMALLINT,
27                                                 admin_level SMALLINT,
28                                                 geometry_sector INTEGER)""")
29             cur.execute("""CREATE TABLE location_property_osmline (
30                                place_id BIGINT,
31                                indexed_status SMALLINT,
32                                indexed_date TIMESTAMP,
33                                geometry_sector INTEGER)""")
34             cur.execute("""CREATE OR REPLACE FUNCTION date_update() RETURNS TRIGGER
35                            AS $$
36                            BEGIN
37                              IF NEW.indexed_status = 0 and OLD.indexed_status != 0 THEN
38                                NEW.indexed_date = now();
39                              END IF;
40                              RETURN NEW;
41                            END; $$ LANGUAGE plpgsql;""")
42             cur.execute("""CREATE TRIGGER placex_update BEFORE UPDATE ON placex
43                            FOR EACH ROW EXECUTE PROCEDURE date_update()""")
44             cur.execute("""CREATE TRIGGER osmline_update BEFORE UPDATE ON location_property_osmline
45                            FOR EACH ROW EXECUTE PROCEDURE date_update()""")
46
47     def scalar(self, query):
48         with self.conn.cursor() as cur:
49             cur.execute(query)
50             return cur.fetchone()[0]
51
52     def add_place(self, cls='place', typ='locality',
53                   rank_search=30, rank_address=30, sector=20):
54         next_id = next(self.placex_id)
55         with self.conn.cursor() as cur:
56             cur.execute("""INSERT INTO placex
57                               (place_id, class, type, rank_search, rank_address,
58                                indexed_status, geometry_sector)
59                               VALUES (%s, %s, %s, %s, %s, 1, %s)""",
60                         (next_id, cls, typ, rank_search, rank_address, sector))
61         return next_id
62
63     def add_admin(self, **kwargs):
64         kwargs['cls'] = 'boundary'
65         kwargs['typ'] = 'administrative'
66         return self.add_place(**kwargs)
67
68     def add_osmline(self, sector=20):
69         next_id = next(self.osmline_id)
70         with self.conn.cursor() as cur:
71             cur.execute("""INSERT INTO location_property_osmline
72                               (place_id, indexed_status, geometry_sector)
73                               VALUES (%s, 1, %s)""",
74                         (next_id, sector))
75         return next_id
76
77     def placex_unindexed(self):
78         return self.scalar('SELECT count(*) from placex where indexed_status > 0')
79
80     def osmline_unindexed(self):
81         return self.scalar('SELECT count(*) from location_property_osmline where indexed_status > 0')
82
83
84 @pytest.fixture
85 def test_db(temp_db_conn):
86     yield IndexerTestDB(temp_db_conn)
87
88
89 @pytest.mark.parametrize("threads", [1, 15])
90 def test_index_full(test_db, threads):
91     for rank in range(31):
92         test_db.add_place(rank_address=rank, rank_search=rank)
93     test_db.add_osmline()
94
95     assert 31 == test_db.placex_unindexed()
96     assert 1 == test_db.osmline_unindexed()
97
98     idx = Indexer('dbname=test_nominatim_python_unittest', threads)
99     idx.index_by_rank(0, 30)
100
101     assert 0 == test_db.placex_unindexed()
102     assert 0 == test_db.osmline_unindexed()
103
104     assert 0 == test_db.scalar("""SELECT count(*) from placex
105                                WHERE indexed_status = 0 and indexed_date is null""")
106     # ranks come in order of rank address
107     assert 0 == test_db.scalar("""
108         SELECT count(*) FROM placex p WHERE rank_address > 0
109           AND indexed_date >= (SELECT min(indexed_date) FROM placex o
110                                WHERE p.rank_address < o.rank_address)""")
111     # placex rank < 30 objects come before interpolations
112     assert 0 == test_db.scalar(
113         """SELECT count(*) FROM placex WHERE rank_address < 30
114              AND indexed_date > (SELECT min(indexed_date) FROM location_property_osmline)""")
115     # placex rank = 30 objects come after interpolations
116     assert 0 == test_db.scalar(
117         """SELECT count(*) FROM placex WHERE rank_address = 30
118              AND indexed_date < (SELECT max(indexed_date) FROM location_property_osmline)""")
119     # rank 0 comes after rank 29 and before rank 30
120     assert 0 == test_db.scalar(
121         """SELECT count(*) FROM placex WHERE rank_address < 30
122              AND indexed_date > (SELECT min(indexed_date) FROM placex WHERE rank_address = 0)""")
123     assert 0 == test_db.scalar(
124         """SELECT count(*) FROM placex WHERE rank_address = 30
125              AND indexed_date < (SELECT max(indexed_date) FROM placex WHERE rank_address = 0)""")
126
127
128 @pytest.mark.parametrize("threads", [1, 15])
129 def test_index_partial_without_30(test_db, threads):
130     for rank in range(31):
131         test_db.add_place(rank_address=rank, rank_search=rank)
132     test_db.add_osmline()
133
134     assert 31 == test_db.placex_unindexed()
135     assert 1 == test_db.osmline_unindexed()
136
137     idx = Indexer('dbname=test_nominatim_python_unittest', threads)
138     idx.index_by_rank(4, 15)
139
140     assert 19 == test_db.placex_unindexed()
141     assert 1 == test_db.osmline_unindexed()
142
143     assert 0 == test_db.scalar("""
144                     SELECT count(*) FROM placex
145                       WHERE indexed_status = 0 AND not rank_address between 4 and 15""")
146
147
148 @pytest.mark.parametrize("threads", [1, 15])
149 def test_index_partial_with_30(test_db, threads):
150     for rank in range(31):
151         test_db.add_place(rank_address=rank, rank_search=rank)
152     test_db.add_osmline()
153
154     assert 31 == test_db.placex_unindexed()
155     assert 1 == test_db.osmline_unindexed()
156
157     idx = Indexer('dbname=test_nominatim_python_unittest', threads)
158     idx.index_by_rank(28, 30)
159
160     assert 27 == test_db.placex_unindexed()
161     assert 0 == test_db.osmline_unindexed()
162
163     assert 0 == test_db.scalar("""
164                     SELECT count(*) FROM placex
165                       WHERE indexed_status = 0 AND rank_address between 1 and 27""")
166
167 @pytest.mark.parametrize("threads", [1, 15])
168 def test_index_boundaries(test_db, threads):
169     for rank in range(4, 10):
170         test_db.add_admin(rank_address=rank, rank_search=rank)
171     for rank in range(31):
172         test_db.add_place(rank_address=rank, rank_search=rank)
173     test_db.add_osmline()
174
175     assert 37 == test_db.placex_unindexed()
176     assert 1 == test_db.osmline_unindexed()
177
178     idx = Indexer('dbname=test_nominatim_python_unittest', threads)
179     idx.index_boundaries(0, 30)
180
181     assert 31 == test_db.placex_unindexed()
182     assert 1 == test_db.osmline_unindexed()
183
184     assert 0 == test_db.scalar("""
185                     SELECT count(*) FROM placex
186                       WHERE indexed_status = 0 AND class != 'boundary'""")