]> git.openstreetmap.org Git - nominatim.git/blob - lib-sql/tokenizer/icu_tokenizer.sql
fix a number of corner cases with interpolation splitting
[nominatim.git] / lib-sql / tokenizer / icu_tokenizer.sql
1 -- SPDX-License-Identifier: GPL-2.0-only
2 --
3 -- This file is part of Nominatim. (https://nominatim.org)
4 --
5 -- Copyright (C) 2022 by the Nominatim developer community.
6 -- For a full list of authors see the git log.
7
8 -- Get tokens used for searching the given place.
9 --
10 -- These are the tokens that will be saved in the search_name table.
11 CREATE OR REPLACE FUNCTION token_get_name_search_tokens(info JSONB)
12   RETURNS INTEGER[]
13 AS $$
14   SELECT (info->>'names')::INTEGER[]
15 $$ LANGUAGE SQL IMMUTABLE STRICT;
16
17
18 -- Get tokens for matching the place name against others.
19 --
20 -- This should usually be restricted to full name tokens.
21 CREATE OR REPLACE FUNCTION token_get_name_match_tokens(info JSONB)
22   RETURNS INTEGER[]
23 AS $$
24   SELECT (info->>'names')::INTEGER[]
25 $$ LANGUAGE SQL IMMUTABLE STRICT;
26
27
28 -- Return the housenumber tokens applicable for the place.
29 CREATE OR REPLACE FUNCTION token_get_housenumber_search_tokens(info JSONB)
30   RETURNS INTEGER[]
31 AS $$
32   SELECT (info->>'hnr_tokens')::INTEGER[]
33 $$ LANGUAGE SQL IMMUTABLE STRICT;
34
35
36 -- Return the housenumber in the form that it can be matched during search.
37 CREATE OR REPLACE FUNCTION token_normalized_housenumber(info JSONB)
38   RETURNS TEXT
39 AS $$
40   SELECT info->>'hnr';
41 $$ LANGUAGE SQL IMMUTABLE STRICT;
42
43
44 CREATE OR REPLACE FUNCTION token_has_addr_street(info JSONB)
45   RETURNS BOOLEAN
46 AS $$
47   SELECT info->>'street' is not null;
48 $$ LANGUAGE SQL IMMUTABLE;
49
50
51 CREATE OR REPLACE FUNCTION token_has_addr_place(info JSONB)
52   RETURNS BOOLEAN
53 AS $$
54   SELECT info->>'place' is not null;
55 $$ LANGUAGE SQL IMMUTABLE;
56
57
58 CREATE OR REPLACE FUNCTION token_matches_street(info JSONB, street_tokens INTEGER[])
59   RETURNS BOOLEAN
60 AS $$
61   SELECT (info->>'street')::INTEGER[] && street_tokens
62 $$ LANGUAGE SQL IMMUTABLE STRICT;
63
64
65 CREATE OR REPLACE FUNCTION token_matches_place(info JSONB, place_tokens INTEGER[])
66   RETURNS BOOLEAN
67 AS $$
68   SELECT (info->>'place')::INTEGER[] <@ place_tokens
69 $$ LANGUAGE SQL IMMUTABLE STRICT;
70
71
72 CREATE OR REPLACE FUNCTION token_addr_place_search_tokens(info JSONB)
73   RETURNS INTEGER[]
74 AS $$
75   SELECT (info->>'place')::INTEGER[]
76 $$ LANGUAGE SQL IMMUTABLE STRICT;
77
78
79 CREATE OR REPLACE FUNCTION token_get_address_keys(info JSONB)
80   RETURNS SETOF TEXT
81 AS $$
82   SELECT * FROM jsonb_object_keys(info->'addr');
83 $$ LANGUAGE SQL IMMUTABLE STRICT;
84
85
86 CREATE OR REPLACE FUNCTION token_get_address_search_tokens(info JSONB, key TEXT)
87   RETURNS INTEGER[]
88 AS $$
89   SELECT (info->'addr'->>key)::INTEGER[];
90 $$ LANGUAGE SQL IMMUTABLE STRICT;
91
92
93 CREATE OR REPLACE FUNCTION token_matches_address(info JSONB, key TEXT, tokens INTEGER[])
94   RETURNS BOOLEAN
95 AS $$
96   SELECT (info->'addr'->>key)::INTEGER[] <@ tokens;
97 $$ LANGUAGE SQL IMMUTABLE STRICT;
98
99
100 CREATE OR REPLACE FUNCTION token_get_postcode(info JSONB)
101   RETURNS TEXT
102 AS $$
103   SELECT info->>'postcode';
104 $$ LANGUAGE SQL IMMUTABLE STRICT;
105
106
107 -- Return token info that should be saved permanently in the database.
108 CREATE OR REPLACE FUNCTION token_strip_info(info JSONB)
109   RETURNS JSONB
110 AS $$
111   SELECT NULL::JSONB;
112 $$ LANGUAGE SQL IMMUTABLE STRICT;
113
114 --------------- private functions ----------------------------------------------
115
116 CREATE OR REPLACE FUNCTION getorcreate_full_word(norm_term TEXT, lookup_terms TEXT[],
117                                                  OUT full_token INT,
118                                                  OUT partial_tokens INT[])
119   AS $$
120 DECLARE
121   partial_terms TEXT[] = '{}'::TEXT[];
122   term TEXT;
123   term_id INTEGER;
124   term_count INTEGER;
125 BEGIN
126   SELECT min(word_id) INTO full_token
127     FROM word WHERE word = norm_term and type = 'W';
128
129   IF full_token IS NULL THEN
130     full_token := nextval('seq_word');
131     INSERT INTO word (word_id, word_token, type, word, info)
132       SELECT full_token, lookup_term, 'W', norm_term,
133              json_build_object('count', 0)
134         FROM unnest(lookup_terms) as lookup_term;
135   END IF;
136
137   FOR term IN SELECT unnest(string_to_array(unnest(lookup_terms), ' ')) LOOP
138     term := trim(term);
139     IF NOT (ARRAY[term] <@ partial_terms) THEN
140       partial_terms := partial_terms || term;
141     END IF;
142   END LOOP;
143
144   partial_tokens := '{}'::INT[];
145   FOR term IN SELECT unnest(partial_terms) LOOP
146     SELECT min(word_id), max(info->>'count') INTO term_id, term_count
147       FROM word WHERE word_token = term and type = 'w';
148
149     IF term_id IS NULL THEN
150       term_id := nextval('seq_word');
151       term_count := 0;
152       INSERT INTO word (word_id, word_token, type, info)
153         VALUES (term_id, term, 'w', json_build_object('count', term_count));
154     END IF;
155
156     partial_tokens := array_merge(partial_tokens, ARRAY[term_id]);
157   END LOOP;
158 END;
159 $$
160 LANGUAGE plpgsql;
161
162
163 CREATE OR REPLACE FUNCTION getorcreate_partial_word(partial TEXT)
164   RETURNS INTEGER
165   AS $$
166 DECLARE
167   token INTEGER;
168 BEGIN
169   SELECT min(word_id) INTO token
170     FROM word WHERE word_token = partial and type = 'w';
171
172   IF token IS NULL THEN
173     token := nextval('seq_word');
174     INSERT INTO word (word_id, word_token, type, info)
175         VALUES (token, partial, 'w', json_build_object('count', 0));
176   END IF;
177
178   RETURN token;
179 END;
180 $$
181 LANGUAGE plpgsql;
182
183
184 CREATE OR REPLACE FUNCTION getorcreate_hnr_id(lookup_term TEXT)
185   RETURNS INTEGER
186   AS $$
187 DECLARE
188   return_id INTEGER;
189 BEGIN
190   SELECT min(word_id) INTO return_id FROM word
191     WHERE word_token = lookup_term and type = 'H';
192
193   IF return_id IS NULL THEN
194     return_id := nextval('seq_word');
195     INSERT INTO word (word_id, word_token, type)
196       VALUES (return_id, lookup_term, 'H');
197   END IF;
198
199   RETURN return_id;
200 END;
201 $$
202 LANGUAGE plpgsql;
203
204
205 CREATE OR REPLACE FUNCTION create_analyzed_hnr_id(norm_term TEXT, lookup_terms TEXT[])
206   RETURNS INTEGER
207   AS $$
208 DECLARE
209   return_id INTEGER;
210 BEGIN
211   SELECT min(word_id) INTO return_id
212     FROM word WHERE word = norm_term and type = 'H';
213
214   IF return_id IS NULL THEN
215     return_id := nextval('seq_word');
216     INSERT INTO word (word_id, word_token, type, word, info)
217       SELECT return_id, lookup_term, 'H', norm_term,
218              json_build_object('lookup', lookup_terms[1])
219         FROM unnest(lookup_terms) as lookup_term;
220   END IF;
221
222   RETURN return_id;
223 END;
224 $$
225 LANGUAGE plpgsql;
226
227 CREATE OR REPLACE FUNCTION create_postcode_word(postcode TEXT, lookup_terms TEXT[])
228   RETURNS BOOLEAN
229   AS $$
230 DECLARE
231   existing INTEGER;
232 BEGIN
233   SELECT count(*) INTO existing
234     FROM word WHERE word = postcode and type = 'P';
235
236   IF existing > 0 THEN
237     RETURN TRUE;
238   END IF;
239
240   -- postcodes don't need word ids
241   INSERT INTO word (word_token, type, word)
242     SELECT lookup_term, 'P', postcode FROM unnest(lookup_terms) as lookup_term;
243
244   RETURN FALSE;
245 END;
246 $$
247 LANGUAGE plpgsql;
248