]> git.openstreetmap.org Git - nominatim.git/blob - lib-sql/tokenizer/legacy_tokenizer.sql
move houseunumber handling to tokenizer
[nominatim.git] / lib-sql / tokenizer / legacy_tokenizer.sql
1 -- Get tokens used for searching the given place.
2 --
3 -- These are the tokens that will be saved in the search_name table.
4 CREATE OR REPLACE FUNCTION token_get_name_search_tokens(info JSONB)
5   RETURNS INTEGER[]
6 AS $$
7   SELECT (info->>'names')::INTEGER[]
8 $$ LANGUAGE SQL IMMUTABLE STRICT;
9
10
11 -- Get tokens for matching the place name against others.
12 --
13 -- This should usually be restricted to full name tokens.
14 CREATE OR REPLACE FUNCTION token_get_name_match_tokens(info JSONB)
15   RETURNS INTEGER[]
16 AS $$
17   SELECT (info->>'names')::INTEGER[]
18 $$ LANGUAGE SQL IMMUTABLE STRICT;
19
20
21 -- Return the housenumber tokens applicable for the place.
22 CREATE OR REPLACE FUNCTION token_get_housenumber_search_tokens(info JSONB)
23   RETURNS INTEGER[]
24 AS $$
25   SELECT (info->>'hnr_tokens')::INTEGER[]
26 $$ LANGUAGE SQL IMMUTABLE STRICT;
27
28
29 -- Return the housenumber in the form that it can be matched during search.
30 CREATE OR REPLACE FUNCTION token_normalized_housenumber(info JSONB)
31   RETURNS TEXT
32 AS $$
33   SELECT info->>'hnr';
34 $$ LANGUAGE SQL IMMUTABLE STRICT;
35
36
37 -- Return token info that should be saved permanently in the database.
38 CREATE OR REPLACE FUNCTION token_strip_info(info JSONB)
39   RETURNS JSONB
40 AS $$
41   SELECT NULL::JSONB;
42 $$ LANGUAGE SQL IMMUTABLE STRICT;
43
44 --------------- private functions ----------------------------------------------
45
46 -- Functions for term normalisation and access to the 'word' table.
47
48 CREATE OR REPLACE FUNCTION transliteration(text) RETURNS text
49   AS '{{ modulepath }}/nominatim.so', 'transliteration'
50 LANGUAGE c IMMUTABLE STRICT;
51
52
53 CREATE OR REPLACE FUNCTION gettokenstring(text) RETURNS text
54   AS '{{ modulepath }}/nominatim.so', 'gettokenstring'
55 LANGUAGE c IMMUTABLE STRICT;
56
57
58 CREATE OR REPLACE FUNCTION make_standard_name(name TEXT) RETURNS TEXT
59   AS $$
60 DECLARE
61   o TEXT;
62 BEGIN
63   o := public.gettokenstring(public.transliteration(name));
64   RETURN trim(substr(o,1,length(o)));
65 END;
66 $$
67 LANGUAGE plpgsql IMMUTABLE;
68
69 -- returns NULL if the word is too common
70 CREATE OR REPLACE FUNCTION getorcreate_word_id(lookup_word TEXT) 
71   RETURNS INTEGER
72   AS $$
73 DECLARE
74   lookup_token TEXT;
75   return_word_id INTEGER;
76   count INTEGER;
77 BEGIN
78   lookup_token := trim(lookup_word);
79   SELECT min(word_id), max(search_name_count) FROM word
80     WHERE word_token = lookup_token and class is null and type is null
81     INTO return_word_id, count;
82   IF return_word_id IS NULL THEN
83     return_word_id := nextval('seq_word');
84     INSERT INTO word VALUES (return_word_id, lookup_token, null, null, null, null, 0);
85   ELSE
86     IF count > {{ max_word_freq }} THEN
87       return_word_id := NULL;
88     END IF;
89   END IF;
90   RETURN return_word_id;
91 END;
92 $$
93 LANGUAGE plpgsql;
94
95
96 -- Create housenumber tokens from an OSM addr:housenumber.
97 -- The housnumber is split at comma and semicolon as necessary.
98 -- The function returns the normalized form of the housenumber suitable
99 -- for comparison.
100 CREATE OR REPLACE FUNCTION create_housenumbers(housenumbers TEXT[],
101                                                OUT tokens TEXT,
102                                                OUT normtext TEXT)
103   AS $$
104 BEGIN
105   SELECT array_to_string(array_agg(trans), ';'), array_agg(tid)::TEXT
106     INTO normtext, tokens
107     FROM (SELECT lookup_word as trans, getorcreate_housenumber_id(lookup_word) as tid
108           FROM (SELECT make_standard_name(h) as lookup_word
109                 FROM unnest(housenumbers) h) x) y;
110 END;
111 $$ LANGUAGE plpgsql STABLE STRICT;
112
113
114 CREATE OR REPLACE FUNCTION getorcreate_housenumber_id(lookup_word TEXT)
115   RETURNS INTEGER
116   AS $$
117 DECLARE
118   lookup_token TEXT;
119   return_word_id INTEGER;
120 BEGIN
121   lookup_token := ' ' || trim(lookup_word);
122   SELECT min(word_id) FROM word
123     WHERE word_token = lookup_token and class='place' and type='house'
124     INTO return_word_id;
125   IF return_word_id IS NULL THEN
126     return_word_id := nextval('seq_word');
127     INSERT INTO word VALUES (return_word_id, lookup_token, null,
128                              'place', 'house', null, 0);
129   END IF;
130   RETURN return_word_id;
131 END;
132 $$
133 LANGUAGE plpgsql;
134
135
136 CREATE OR REPLACE FUNCTION getorcreate_postcode_id(postcode TEXT)
137   RETURNS INTEGER
138   AS $$
139 DECLARE
140   lookup_token TEXT;
141   lookup_word TEXT;
142   return_word_id INTEGER;
143 BEGIN
144   lookup_word := upper(trim(postcode));
145   lookup_token := ' ' || make_standard_name(lookup_word);
146   SELECT min(word_id) FROM word
147     WHERE word_token = lookup_token and word = lookup_word
148           and class='place' and type='postcode'
149     INTO return_word_id;
150   IF return_word_id IS NULL THEN
151     return_word_id := nextval('seq_word');
152     INSERT INTO word VALUES (return_word_id, lookup_token, lookup_word,
153                              'place', 'postcode', null, 0);
154   END IF;
155   RETURN return_word_id;
156 END;
157 $$
158 LANGUAGE plpgsql;
159
160
161 CREATE OR REPLACE FUNCTION getorcreate_country(lookup_word TEXT,
162                                                lookup_country_code varchar(2))
163   RETURNS INTEGER
164   AS $$
165 DECLARE
166   lookup_token TEXT;
167   return_word_id INTEGER;
168 BEGIN
169   lookup_token := ' '||trim(lookup_word);
170   SELECT min(word_id) FROM word
171     WHERE word_token = lookup_token and country_code=lookup_country_code
172     INTO return_word_id;
173   IF return_word_id IS NULL THEN
174     return_word_id := nextval('seq_word');
175     INSERT INTO word VALUES (return_word_id, lookup_token, null,
176                              null, null, lookup_country_code, 0);
177   END IF;
178   RETURN return_word_id;
179 END;
180 $$
181 LANGUAGE plpgsql;
182
183
184 CREATE OR REPLACE FUNCTION getorcreate_amenity(lookup_word TEXT, normalized_word TEXT,
185                                                lookup_class text, lookup_type text)
186   RETURNS INTEGER
187   AS $$
188 DECLARE
189   lookup_token TEXT;
190   return_word_id INTEGER;
191 BEGIN
192   lookup_token := ' '||trim(lookup_word);
193   SELECT min(word_id) FROM word
194   WHERE word_token = lookup_token and word = normalized_word
195         and class = lookup_class and type = lookup_type
196   INTO return_word_id;
197   IF return_word_id IS NULL THEN
198     return_word_id := nextval('seq_word');
199     INSERT INTO word VALUES (return_word_id, lookup_token, normalized_word,
200                              lookup_class, lookup_type, null, 0);
201   END IF;
202   RETURN return_word_id;
203 END;
204 $$
205 LANGUAGE plpgsql;
206
207
208 CREATE OR REPLACE FUNCTION getorcreate_amenityoperator(lookup_word TEXT,
209                                                        normalized_word TEXT,
210                                                        lookup_class text,
211                                                        lookup_type text,
212                                                        op text)
213   RETURNS INTEGER
214   AS $$
215 DECLARE
216   lookup_token TEXT;
217   return_word_id INTEGER;
218 BEGIN
219   lookup_token := ' '||trim(lookup_word);
220   SELECT min(word_id) FROM word
221   WHERE word_token = lookup_token and word = normalized_word
222         and class = lookup_class and type = lookup_type and operator = op
223   INTO return_word_id;
224   IF return_word_id IS NULL THEN
225     return_word_id := nextval('seq_word');
226     INSERT INTO word VALUES (return_word_id, lookup_token, normalized_word,
227                              lookup_class, lookup_type, null, 0, op);
228   END IF;
229   RETURN return_word_id;
230 END;
231 $$
232 LANGUAGE plpgsql;
233
234
235 CREATE OR REPLACE FUNCTION getorcreate_name_id(lookup_word TEXT, src_word TEXT)
236   RETURNS INTEGER
237   AS $$
238 DECLARE
239   lookup_token TEXT;
240   nospace_lookup_token TEXT;
241   return_word_id INTEGER;
242 BEGIN
243   lookup_token := ' '||trim(lookup_word);
244   SELECT min(word_id) FROM word
245   WHERE word_token = lookup_token and class is null and type is null
246   INTO return_word_id;
247   IF return_word_id IS NULL THEN
248     return_word_id := nextval('seq_word');
249     INSERT INTO word VALUES (return_word_id, lookup_token, src_word,
250                              null, null, null, 0);
251   END IF;
252   RETURN return_word_id;
253 END;
254 $$
255 LANGUAGE plpgsql;
256
257
258 CREATE OR REPLACE FUNCTION getorcreate_name_id(lookup_word TEXT)
259   RETURNS INTEGER
260   AS $$
261 DECLARE
262 BEGIN
263   RETURN getorcreate_name_id(lookup_word, '');
264 END;
265 $$
266 LANGUAGE plpgsql;
267
268 -- Normalize a string and lookup its word ids (partial words).
269 CREATE OR REPLACE FUNCTION addr_ids_from_name(lookup_word TEXT)
270   RETURNS INTEGER[]
271   AS $$
272 DECLARE
273   words TEXT[];
274   id INTEGER;
275   return_word_id INTEGER[];
276   word_ids INTEGER[];
277   j INTEGER;
278 BEGIN
279   words := string_to_array(make_standard_name(lookup_word), ' ');
280   IF array_upper(words, 1) IS NOT NULL THEN
281     FOR j IN 1..array_upper(words, 1) LOOP
282       IF (words[j] != '') THEN
283         SELECT array_agg(word_id) INTO word_ids
284           FROM word
285          WHERE word_token = words[j] and class is null and type is null;
286
287         IF word_ids IS NULL THEN
288           id := nextval('seq_word');
289           INSERT INTO word VALUES (id, words[j], null, null, null, null, 0);
290           return_word_id := return_word_id || id;
291         ELSE
292           return_word_id := array_merge(return_word_id, word_ids);
293         END IF;
294       END IF;
295     END LOOP;
296   END IF;
297
298   RETURN return_word_id;
299 END;
300 $$
301 LANGUAGE plpgsql;
302
303
304 -- Normalize a string and look up its name ids (full words).
305 CREATE OR REPLACE FUNCTION word_ids_from_name(lookup_word TEXT)
306   RETURNS INTEGER[]
307   AS $$
308 DECLARE
309   lookup_token TEXT;
310   return_word_ids INTEGER[];
311 BEGIN
312   lookup_token := ' '|| make_standard_name(lookup_word);
313   SELECT array_agg(word_id) FROM word
314     WHERE word_token = lookup_token and class is null and type is null
315     INTO return_word_ids;
316   RETURN return_word_ids;
317 END;
318 $$
319 LANGUAGE plpgsql STABLE STRICT;
320
321
322 CREATE OR REPLACE FUNCTION create_country(src HSTORE, country_code varchar(2))
323   RETURNS VOID
324   AS $$
325 DECLARE
326   s TEXT;
327   w INTEGER;
328   words TEXT[];
329   item RECORD;
330   j INTEGER;
331 BEGIN
332   FOR item IN SELECT (each(src)).* LOOP
333
334     s := make_standard_name(item.value);
335     w := getorcreate_country(s, country_code);
336
337     words := regexp_split_to_array(item.value, E'[,;()]');
338     IF array_upper(words, 1) != 1 THEN
339       FOR j IN 1..array_upper(words, 1) LOOP
340         s := make_standard_name(words[j]);
341         IF s != '' THEN
342           w := getorcreate_country(s, country_code);
343         END IF;
344       END LOOP;
345     END IF;
346   END LOOP;
347 END;
348 $$
349 LANGUAGE plpgsql;
350
351
352 CREATE OR REPLACE FUNCTION make_keywords(src HSTORE)
353   RETURNS INTEGER[]
354   AS $$
355 DECLARE
356   result INTEGER[];
357   s TEXT;
358   w INTEGER;
359   words TEXT[];
360   item RECORD;
361   j INTEGER;
362 BEGIN
363   result := '{}'::INTEGER[];
364
365   FOR item IN SELECT (each(src)).* LOOP
366
367     s := make_standard_name(item.value);
368     w := getorcreate_name_id(s, item.value);
369
370     IF not(ARRAY[w] <@ result) THEN
371       result := result || w;
372     END IF;
373
374     w := getorcreate_word_id(s);
375
376     IF w IS NOT NULL AND NOT (ARRAY[w] <@ result) THEN
377       result := result || w;
378     END IF;
379
380     words := string_to_array(s, ' ');
381     IF array_upper(words, 1) IS NOT NULL THEN
382       FOR j IN 1..array_upper(words, 1) LOOP
383         IF (words[j] != '') THEN
384           w = getorcreate_word_id(words[j]);
385           IF w IS NOT NULL AND NOT (ARRAY[w] <@ result) THEN
386             result := result || w;
387           END IF;
388         END IF;
389       END LOOP;
390     END IF;
391
392     words := regexp_split_to_array(item.value, E'[,;()]');
393     IF array_upper(words, 1) != 1 THEN
394       FOR j IN 1..array_upper(words, 1) LOOP
395         s := make_standard_name(words[j]);
396         IF s != '' THEN
397           w := getorcreate_word_id(s);
398           IF w IS NOT NULL AND NOT (ARRAY[w] <@ result) THEN
399             result := result || w;
400           END IF;
401         END IF;
402       END LOOP;
403     END IF;
404
405     s := regexp_replace(item.value, '市$', '');
406     IF s != item.value THEN
407       s := make_standard_name(s);
408       IF s != '' THEN
409         w := getorcreate_name_id(s, item.value);
410         IF NOT (ARRAY[w] <@ result) THEN
411           result := result || w;
412         END IF;
413       END IF;
414     END IF;
415
416   END LOOP;
417
418   RETURN result;
419 END;
420 $$
421 LANGUAGE plpgsql;
422
423
424 CREATE OR REPLACE FUNCTION precompute_words(src TEXT)
425   RETURNS INTEGER
426   AS $$
427 DECLARE
428   s TEXT;
429   w INTEGER;
430   words TEXT[];
431   i INTEGER;
432   j INTEGER;
433 BEGIN
434   s := make_standard_name(src);
435   w := getorcreate_name_id(s, src);
436
437   w := getorcreate_word_id(s);
438
439   words := string_to_array(s, ' ');
440   IF array_upper(words, 1) IS NOT NULL THEN
441     FOR j IN 1..array_upper(words, 1) LOOP
442       IF (words[j] != '') THEN
443         w := getorcreate_word_id(words[j]);
444       END IF;
445     END LOOP;
446   END IF;
447
448   words := regexp_split_to_array(src, E'[,;()]');
449   IF array_upper(words, 1) != 1 THEN
450     FOR j IN 1..array_upper(words, 1) LOOP
451       s := make_standard_name(words[j]);
452       IF s != '' THEN
453         w := getorcreate_word_id(s);
454       END IF;
455     END LOOP;
456   END IF;
457
458   s := regexp_replace(src, '市$', '');
459   IF s != src THEN
460     s := make_standard_name(s);
461     IF s != '' THEN
462       w := getorcreate_name_id(s, src);
463     END IF;
464   END IF;
465
466   RETURN 1;
467 END;
468 $$
469 LANGUAGE plpgsql;