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