]> git.openstreetmap.org Git - nominatim.git/blob - src/nominatim_db/tokenizer/base.py
release 5.3.2.post11
[nominatim.git] / src / nominatim_db / tokenizer / base.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 Abstract class definitions for tokenizers. These base classes are here
9 mainly for documentation purposes.
10 """
11 from abc import ABC, abstractmethod
12 from typing import List, Tuple, Any, Optional, Iterable
13
14 from ..typing import Protocol
15 from ..config import Configuration
16 from ..db.connection import Connection
17 from ..data.place_info import PlaceInfo
18 from ..data.place_name import PlaceNames
19
20
21 class AbstractAnalyzer(ABC):
22     """ The analyzer provides the functions for analysing names and building
23         the token database.
24
25         Analyzers are instantiated on a per-thread base. Access to global data
26         structures must be synchronised accordingly.
27     """
28
29     def __enter__(self) -> 'AbstractAnalyzer':
30         return self
31
32     def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
33         self.close()
34
35     @abstractmethod
36     def close(self) -> None:
37         """ Free all resources used by the analyzer.
38         """
39
40     @abstractmethod
41     def get_word_token_info(self, words: List[str]) -> List[Tuple[str, str, Optional[int]]]:
42         """ Return token information for the given list of words.
43
44             The function is used for testing and debugging only
45             and does not need to be particularly efficient.
46
47             Arguments:
48                 words: A list of words to look up the tokens for.
49                        If a word starts with # it is assumed to be a full name
50                        otherwise is a partial term.
51
52             Returns:
53                 The function returns the list of all tuples that could be
54                     found for the given words. Each list entry is a tuple of
55                     (original word, word token, word id).
56         """
57
58     @abstractmethod
59     def normalize_postcode(self, postcode: str) -> str:
60         """ Convert the postcode to its standardized form.
61
62             Arguments:
63                 postcode: The postcode to be normalized.
64
65             Returns:
66                 The given postcode after normalization.
67         """
68
69     @abstractmethod
70     def update_postcodes_from_db(self) -> None:
71         """ Update the tokenizer's postcode tokens from the current content
72             of the `location_postcodes` table.
73         """
74
75     @abstractmethod
76     def update_special_phrases(self,
77                                phrases: Iterable[Tuple[str, str, str, str]],
78                                should_replace: bool) -> None:
79         """ Update the tokenizer's special phrase tokens from the given
80             list of special phrases.
81
82             Arguments:
83                 phrases: The new list of special phrases. Each entry is
84                          a tuple of (phrase, class, type, operator).
85                 should_replace: If true, replace the current list of phrases.
86                                 When false, just add the given phrases to the
87                                 ones that already exist.
88         """
89
90     @abstractmethod
91     def add_country_names(self, country_code: str, names: PlaceNames) -> None:
92         """ Add the given names to the tokenizer's list of country tokens.
93
94             Arguments:
95                 country_code: two-letter country code for the country the names
96                               refer to.
97                 names: List of names to add.
98         """
99
100     @abstractmethod
101     def process_place(self, place: PlaceInfo) -> Any:
102         """ Extract tokens for the given place and compute the
103             information to be handed to the PL/pgSQL processor for building
104             the search index.
105
106             Arguments:
107                 place: Place information retrieved from the database.
108
109             Returns:
110                 A JSON-serialisable structure that will be handed into
111                     the database via the `token_info` field.
112         """
113
114
115 class AbstractTokenizer(ABC):
116     """ The tokenizer instance is the central instance of the tokenizer in
117         the system. There will only be a single instance of the tokenizer
118         active at any time.
119     """
120
121     @abstractmethod
122     def init_new_db(self, config: Configuration, init_db: bool = True) -> None:
123         """ Set up a new tokenizer for the database.
124
125             The function should copy all necessary data into the project
126             directory or save it in the property table to make sure that
127             the tokenizer remains stable over updates.
128
129             Arguments:
130               config: Read-only object with configuration options.
131
132               init_db: When set to False, then initialisation of database
133                 tables should be skipped. This option is only required for
134                 migration purposes and can be safely ignored by custom
135                 tokenizers.
136         """
137
138     @abstractmethod
139     def init_from_project(self, config: Configuration) -> None:
140         """ Initialise the tokenizer from an existing database setup.
141
142             The function should load all previously saved configuration from
143             the project directory and/or the property table.
144
145             Arguments:
146               config: Read-only object with configuration options.
147         """
148
149     @abstractmethod
150     def finalize_import(self, config: Configuration, threads: int = 1) -> None:
151         """ This function is called at the very end of an import when all
152             data has been imported and indexed. The tokenizer may create
153             at this point any additional indexes and data structures needed
154             during query time.
155
156             Arguments:
157               config: Read-only object with configuration options.
158               threads: Number of threads to use
159         """
160
161     @abstractmethod
162     def update_sql_functions(self, config: Configuration) -> None:
163         """ Update the SQL part of the tokenizer. This function is called
164             automatically on migrations or may be called explicitly by the
165             user through the `nominatim refresh --functions` command.
166
167             The tokenizer must only update the code of the tokenizer. The
168             data structures or data itself must not be changed by this function.
169
170             Arguments:
171               config: Read-only object with configuration options.
172         """
173
174     @abstractmethod
175     def check_database(self, config: Configuration) -> Optional[str]:
176         """ Check that the database is set up correctly and ready for being
177             queried.
178
179             Arguments:
180               config: Read-only object with configuration options.
181
182             Returns:
183               If an issue was found, return an error message with the
184                   description of the issue as well as hints for the user on
185                   how to resolve the issue. If everything is okay, return `None`.
186         """
187
188     @abstractmethod
189     def update_statistics(self, config: Configuration, threads: int = 1) -> None:
190         """ Recompute any tokenizer statistics necessary for efficient lookup.
191             This function is meant to be called from time to time by the user
192             to improve performance. However, the tokenizer must not depend on
193             it to be called in order to work.
194         """
195
196     @abstractmethod
197     def update_word_tokens(self) -> None:
198         """ Do house-keeping on the tokenizers internal data structures.
199             Remove unused word tokens, resort data etc.
200         """
201
202     @abstractmethod
203     def name_analyzer(self) -> AbstractAnalyzer:
204         """ Create a new analyzer for tokenizing names and queries
205             using this tokinzer. Analyzers are context managers and should
206             be used accordingly:
207
208             ```
209             with tokenizer.name_analyzer() as analyzer:
210                 analyser.tokenize()
211             ```
212
213             When used outside the with construct, the caller must ensure to
214             call the close() function before destructing the analyzer.
215         """
216
217     @abstractmethod
218     def most_frequent_words(self, conn: Connection, num: int) -> List[str]:
219         """ Return a list of the most frequent full words in the database.
220
221             Arguments:
222               conn: Open connection to the database which may be used to
223                     retrieve the words.
224               num: Maximum number of words to return.
225         """
226
227
228 class TokenizerModule(Protocol):
229     """ Interface that must be exported by modules that implement their
230         own tokenizer.
231     """
232
233     def create(self, dsn: str) -> AbstractTokenizer:
234         """ Factory for new tokenizers.
235         """