]> git.openstreetmap.org Git - nominatim.git/blob - sql/functions/normalization.sql
linked centroids must always be within geometry
[nominatim.git] / sql / functions / normalization.sql
1 -- Functions for term normalisation and access to the 'word' table.
2
3 CREATE OR REPLACE FUNCTION transliteration(text) RETURNS text
4   AS '{modulepath}/nominatim.so', 'transliteration'
5 LANGUAGE c IMMUTABLE STRICT;
6
7
8 CREATE OR REPLACE FUNCTION gettokenstring(text) RETURNS text
9   AS '{modulepath}/nominatim.so', 'gettokenstring'
10 LANGUAGE c IMMUTABLE STRICT;
11
12
13 CREATE OR REPLACE FUNCTION make_standard_name(name TEXT) RETURNS TEXT
14   AS $$
15 DECLARE
16   o TEXT;
17 BEGIN
18   o := public.gettokenstring(public.transliteration(name));
19   RETURN trim(substr(o,1,length(o)));
20 END;
21 $$
22 LANGUAGE plpgsql IMMUTABLE;
23
24 -- returns NULL if the word is too common
25 CREATE OR REPLACE FUNCTION getorcreate_word_id(lookup_word TEXT) 
26   RETURNS INTEGER
27   AS $$
28 DECLARE
29   lookup_token TEXT;
30   return_word_id INTEGER;
31   count INTEGER;
32 BEGIN
33   lookup_token := trim(lookup_word);
34   SELECT min(word_id), max(search_name_count) FROM word
35     WHERE word_token = lookup_token and class is null and type is null
36     INTO return_word_id, count;
37   IF return_word_id IS NULL THEN
38     return_word_id := nextval('seq_word');
39     INSERT INTO word VALUES (return_word_id, lookup_token, null, null, null, null, 0);
40   ELSE
41     IF count > get_maxwordfreq() THEN
42       return_word_id := NULL;
43     END IF;
44   END IF;
45   RETURN return_word_id;
46 END;
47 $$
48 LANGUAGE plpgsql;
49
50
51 CREATE OR REPLACE FUNCTION getorcreate_housenumber_id(lookup_word TEXT)
52   RETURNS INTEGER
53   AS $$
54 DECLARE
55   lookup_token TEXT;
56   return_word_id INTEGER;
57 BEGIN
58   lookup_token := ' ' || trim(lookup_word);
59   SELECT min(word_id) FROM word
60     WHERE word_token = lookup_token and class='place' and type='house'
61     INTO return_word_id;
62   IF return_word_id IS NULL THEN
63     return_word_id := nextval('seq_word');
64     INSERT INTO word VALUES (return_word_id, lookup_token, null,
65                              'place', 'house', null, 0);
66   END IF;
67   RETURN return_word_id;
68 END;
69 $$
70 LANGUAGE plpgsql;
71
72
73 CREATE OR REPLACE FUNCTION getorcreate_postcode_id(postcode TEXT)
74   RETURNS INTEGER
75   AS $$
76 DECLARE
77   lookup_token TEXT;
78   lookup_word TEXT;
79   return_word_id INTEGER;
80 BEGIN
81   lookup_word := upper(trim(postcode));
82   lookup_token := ' ' || make_standard_name(lookup_word);
83   SELECT min(word_id) FROM word
84     WHERE word_token = lookup_token and class='place' and type='postcode'
85     INTO return_word_id;
86   IF return_word_id IS NULL THEN
87     return_word_id := nextval('seq_word');
88     INSERT INTO word VALUES (return_word_id, lookup_token, lookup_word,
89                              'place', 'postcode', null, 0);
90   END IF;
91   RETURN return_word_id;
92 END;
93 $$
94 LANGUAGE plpgsql;
95
96
97 CREATE OR REPLACE FUNCTION getorcreate_country(lookup_word TEXT,
98                                                lookup_country_code varchar(2))
99   RETURNS INTEGER
100   AS $$
101 DECLARE
102   lookup_token TEXT;
103   return_word_id INTEGER;
104 BEGIN
105   lookup_token := ' '||trim(lookup_word);
106   SELECT min(word_id) FROM word
107     WHERE word_token = lookup_token and country_code=lookup_country_code
108     INTO return_word_id;
109   IF return_word_id IS NULL THEN
110     return_word_id := nextval('seq_word');
111     INSERT INTO word VALUES (return_word_id, lookup_token, null,
112                              null, null, lookup_country_code, 0);
113   END IF;
114   RETURN return_word_id;
115 END;
116 $$
117 LANGUAGE plpgsql;
118
119
120 CREATE OR REPLACE FUNCTION getorcreate_amenity(lookup_word TEXT, normalized_word TEXT,
121                                                lookup_class text, lookup_type 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 word = normalized_word
131         and class = lookup_class and type = lookup_type
132   INTO return_word_id;
133   IF return_word_id IS NULL THEN
134     return_word_id := nextval('seq_word');
135     INSERT INTO word VALUES (return_word_id, lookup_token, normalized_word,
136                              lookup_class, lookup_type, null, 0);
137   END IF;
138   RETURN return_word_id;
139 END;
140 $$
141 LANGUAGE plpgsql;
142
143
144 CREATE OR REPLACE FUNCTION getorcreate_amenityoperator(lookup_word TEXT,
145                                                        normalized_word TEXT,
146                                                        lookup_class text,
147                                                        lookup_type text,
148                                                        op text)
149   RETURNS INTEGER
150   AS $$
151 DECLARE
152   lookup_token TEXT;
153   return_word_id INTEGER;
154 BEGIN
155   lookup_token := ' '||trim(lookup_word);
156   SELECT min(word_id) FROM word
157   WHERE word_token = lookup_token and word = normalized_word
158         and class = lookup_class and type = lookup_type and operator = op
159   INTO return_word_id;
160   IF return_word_id IS NULL THEN
161     return_word_id := nextval('seq_word');
162     INSERT INTO word VALUES (return_word_id, lookup_token, normalized_word,
163                              lookup_class, lookup_type, null, 0, op);
164   END IF;
165   RETURN return_word_id;
166 END;
167 $$
168 LANGUAGE plpgsql;
169
170
171 CREATE OR REPLACE FUNCTION getorcreate_name_id(lookup_word TEXT, src_word TEXT)
172   RETURNS INTEGER
173   AS $$
174 DECLARE
175   lookup_token TEXT;
176   nospace_lookup_token TEXT;
177   return_word_id INTEGER;
178 BEGIN
179   lookup_token := ' '||trim(lookup_word);
180   SELECT min(word_id) FROM word
181   WHERE word_token = lookup_token and class is null and type is null
182   INTO return_word_id;
183   IF return_word_id IS NULL THEN
184     return_word_id := nextval('seq_word');
185     INSERT INTO word VALUES (return_word_id, lookup_token, src_word,
186                              null, null, null, 0);
187   END IF;
188   RETURN return_word_id;
189 END;
190 $$
191 LANGUAGE plpgsql;
192
193
194 CREATE OR REPLACE FUNCTION getorcreate_name_id(lookup_word TEXT)
195   RETURNS INTEGER
196   AS $$
197 DECLARE
198 BEGIN
199   RETURN getorcreate_name_id(lookup_word, '');
200 END;
201 $$
202 LANGUAGE plpgsql;
203
204 -- Normalize a string and lookup its word ids (partial words).
205 CREATE OR REPLACE FUNCTION addr_ids_from_name(lookup_word TEXT)
206   RETURNS INTEGER[]
207   AS $$
208 DECLARE
209   lookup_token TEXT;
210   return_word_id INTEGER[];
211 BEGIN
212   lookup_token := make_standard_name(lookup_word);
213   SELECT array_agg(word_id) FROM word
214     WHERE word_token = lookup_token and class is null and type is null
215     INTO return_word_id;
216   RETURN return_word_id;
217 END;
218 $$
219 LANGUAGE plpgsql STABLE;
220
221
222 -- Normalize a string and look up its name ids (full words).
223 CREATE OR REPLACE FUNCTION word_ids_from_name(lookup_word TEXT)
224   RETURNS INTEGER[]
225   AS $$
226 DECLARE
227   lookup_token TEXT;
228   return_word_ids INTEGER[];
229 BEGIN
230   lookup_token := ' '|| make_standard_name(lookup_word);
231   SELECT array_agg(word_id) FROM word
232     WHERE word_token = lookup_token and class is null and type is null
233     INTO return_word_ids;
234   RETURN return_word_ids;
235 END;
236 $$
237 LANGUAGE plpgsql STABLE STRICT;
238
239
240 CREATE OR REPLACE FUNCTION create_country(src HSTORE, country_code varchar(2))
241   RETURNS VOID
242   AS $$
243 DECLARE
244   s TEXT;
245   w INTEGER;
246   words TEXT[];
247   item RECORD;
248   j INTEGER;
249 BEGIN
250   FOR item IN SELECT (each(src)).* LOOP
251
252     s := make_standard_name(item.value);
253     w := getorcreate_country(s, country_code);
254
255     words := regexp_split_to_array(item.value, E'[,;()]');
256     IF array_upper(words, 1) != 1 THEN
257       FOR j IN 1..array_upper(words, 1) LOOP
258         s := make_standard_name(words[j]);
259         IF s != '' THEN
260           w := getorcreate_country(s, country_code);
261         END IF;
262       END LOOP;
263     END IF;
264   END LOOP;
265 END;
266 $$
267 LANGUAGE plpgsql;
268
269
270 CREATE OR REPLACE FUNCTION make_keywords(src HSTORE)
271   RETURNS INTEGER[]
272   AS $$
273 DECLARE
274   result INTEGER[];
275   s TEXT;
276   w INTEGER;
277   words TEXT[];
278   item RECORD;
279   j INTEGER;
280 BEGIN
281   result := '{}'::INTEGER[];
282
283   FOR item IN SELECT (each(src)).* LOOP
284
285     s := make_standard_name(item.value);
286     w := getorcreate_name_id(s, item.value);
287
288     IF not(ARRAY[w] <@ result) THEN
289       result := result || w;
290     END IF;
291
292     w := getorcreate_word_id(s);
293
294     IF w IS NOT NULL AND NOT (ARRAY[w] <@ result) THEN
295       result := result || w;
296     END IF;
297
298     words := string_to_array(s, ' ');
299     IF array_upper(words, 1) IS NOT NULL THEN
300       FOR j IN 1..array_upper(words, 1) LOOP
301         IF (words[j] != '') THEN
302           w = getorcreate_word_id(words[j]);
303           IF w IS NOT NULL AND NOT (ARRAY[w] <@ result) THEN
304             result := result || w;
305           END IF;
306         END IF;
307       END LOOP;
308     END IF;
309
310     words := regexp_split_to_array(item.value, E'[,;()]');
311     IF array_upper(words, 1) != 1 THEN
312       FOR j IN 1..array_upper(words, 1) LOOP
313         s := make_standard_name(words[j]);
314         IF s != '' THEN
315           w := getorcreate_word_id(s);
316           IF w IS NOT NULL AND NOT (ARRAY[w] <@ result) THEN
317             result := result || w;
318           END IF;
319         END IF;
320       END LOOP;
321     END IF;
322
323     s := regexp_replace(item.value, '市$', '');
324     IF s != item.value THEN
325       s := make_standard_name(s);
326       IF s != '' THEN
327         w := getorcreate_name_id(s, item.value);
328         IF NOT (ARRAY[w] <@ result) THEN
329           result := result || w;
330         END IF;
331       END IF;
332     END IF;
333
334   END LOOP;
335
336   RETURN result;
337 END;
338 $$
339 LANGUAGE plpgsql;
340
341
342 CREATE OR REPLACE FUNCTION make_keywords(src TEXT)
343   RETURNS INTEGER[]
344   AS $$
345 DECLARE
346   result INTEGER[];
347   s TEXT;
348   w INTEGER;
349   words TEXT[];
350   i INTEGER;
351   j INTEGER;
352 BEGIN
353   result := '{}'::INTEGER[];
354
355   s := make_standard_name(src);
356   w := getorcreate_name_id(s, src);
357
358   IF NOT (ARRAY[w] <@ result) THEN
359     result := result || w;
360   END IF;
361
362   w := getorcreate_word_id(s);
363
364   IF w IS NOT NULL AND NOT (ARRAY[w] <@ result) THEN
365     result := result || w;
366   END IF;
367
368   words := string_to_array(s, ' ');
369   IF array_upper(words, 1) IS NOT NULL THEN
370     FOR j IN 1..array_upper(words, 1) LOOP
371       IF (words[j] != '') THEN
372         w = getorcreate_word_id(words[j]);
373         IF w IS NOT NULL AND NOT (ARRAY[w] <@ result) THEN
374           result := result || w;
375         END IF;
376       END IF;
377     END LOOP;
378   END IF;
379
380   words := regexp_split_to_array(src, E'[,;()]');
381   IF array_upper(words, 1) != 1 THEN
382     FOR j IN 1..array_upper(words, 1) LOOP
383       s := make_standard_name(words[j]);
384       IF s != '' THEN
385         w := getorcreate_word_id(s);
386         IF w IS NOT NULL AND NOT (ARRAY[w] <@ result) THEN
387           result := result || w;
388         END IF;
389       END IF;
390     END LOOP;
391   END IF;
392
393   s := regexp_replace(src, '市$', '');
394   IF s != src THEN
395     s := make_standard_name(s);
396     IF s != '' THEN
397       w := getorcreate_name_id(s, src);
398       IF NOT (ARRAY[w] <@ result) THEN
399         result := result || w;
400       END IF;
401     END IF;
402   END IF;
403
404   RETURN result;
405 END;
406 $$
407 LANGUAGE plpgsql;