X-Git-Url: https://git.openstreetmap.org./nominatim.git/blobdiff_plain/2148d81474728c4d59100f8d281ca0dfbe5e7ac2..399cb31507ce9c53e1be2b8606a9070c2bc132fb:/sql/functions.sql?ds=sidebyside diff --git a/sql/functions.sql b/sql/functions.sql index c6b76ad1..6ad13892 100644 --- a/sql/functions.sql +++ b/sql/functions.sql @@ -302,7 +302,7 @@ BEGIN END IF; r := a; FOR i IN 1..array_upper(b, 1) LOOP - IF NOT (ARRAY[b[i]] && r) THEN + IF NOT (ARRAY[b[i]] <@ r) THEN r := r || b[i]; END IF; END LOOP; @@ -328,15 +328,18 @@ BEGIN s := make_standard_name(item.value); w := getorcreate_name_id(s, item.value); - result := result | w; + + IF not(ARRAY[w] <@ result) THEN + result := result || w; + END IF; words := string_to_array(s, ' '); IF array_upper(words, 1) IS NOT NULL THEN FOR j IN 1..array_upper(words, 1) LOOP IF (words[j] != '') THEN w = getorcreate_word_id(words[j]); - IF NOT (ARRAY[w] && result) THEN - result := result | w; + IF NOT (ARRAY[w] <@ result) THEN + result := result || w; END IF; END IF; END LOOP; @@ -348,8 +351,8 @@ BEGIN s := make_standard_name(words[j]); IF s != '' THEN w := getorcreate_word_id(s); - IF NOT (ARRAY[w] && result) THEN - result := result | w; + IF NOT (ARRAY[w] <@ result) THEN + result := result || w; END IF; END IF; END LOOP; @@ -360,8 +363,8 @@ BEGIN s := make_standard_name(s); IF s != '' THEN w := getorcreate_name_id(s, item.value); - IF NOT (ARRAY[w] && result) THEN - result := result | w; + IF NOT (ARRAY[w] <@ result) THEN + result := result || w; END IF; END IF; END IF; @@ -388,7 +391,7 @@ BEGIN s := make_standard_name(src); w := getorcreate_name_id(s); - IF NOT (ARRAY[w] && result) THEN + IF NOT (ARRAY[w] <@ result) THEN result := result || w; END IF; @@ -397,7 +400,7 @@ BEGIN FOR j IN 1..array_upper(words, 1) LOOP IF (words[j] != '') THEN w = getorcreate_word_id(words[j]); - IF NOT (ARRAY[w] && result) THEN + IF NOT (ARRAY[w] <@ result) THEN result := result || w; END IF; END IF; @@ -537,7 +540,7 @@ END; $$ LANGUAGE plpgsql IMMUTABLE; -CREATE OR REPLACE FUNCTION delete_location(OLD_place_id INTEGER) RETURNS BOOLEAN +CREATE OR REPLACE FUNCTION delete_location(OLD_place_id BIGINT) RETURNS BOOLEAN AS $$ DECLARE BEGIN @@ -549,7 +552,7 @@ $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION add_location( - place_id INTEGER, + place_id BIGINT, country_code varchar(2), partition INTEGER, keywords INTEGER[], @@ -654,7 +657,7 @@ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION update_location( partition INTEGER, - place_id INTEGER, + place_id BIGINT, place_country_code varchar(2), name hstore, rank_search INTEGER, @@ -673,7 +676,7 @@ END; $$ LANGUAGE plpgsql; -CREATE OR REPLACE FUNCTION search_name_add_words(parent_place_id INTEGER, to_add INTEGER[]) +CREATE OR REPLACE FUNCTION search_name_add_words(parent_place_id BIGINT, to_add INTEGER[]) RETURNS BOOLEAN AS $$ DECLARE @@ -690,9 +693,11 @@ BEGIN and search_name.place_id = place_addressline.place_id LOOP delete from search_name where place_id = childplace.place_id; - childplace.nameaddress_vector := uniq(sort_asc(childplace.nameaddress_vector + to_add)); - IF childplace.place_id = parent_place_id THEN - childplace.name_vector := uniq(sort_asc(childplace.name_vector + to_add)); + IF not (ARRAY[to_add] <@ childplace.nameaddress_vector) THEN + childplace.nameaddress_vector := childplace.nameaddress_vector || to_add; + END IF; + IF childplace.place_id = parent_place_id and not (ARRAY[to_add] <@ childplace.name_vector) THEN + childplace.name_vector := childplace.name_vector || to_add; END IF; insert into search_name (place_id, search_rank, address_rank, country_code, name_vector, nameaddress_vector, centroid) values (childplace.place_id, childplace.search_rank, childplace.address_rank, childplace.country_code, @@ -704,7 +709,7 @@ END; $$ LANGUAGE plpgsql; -CREATE OR REPLACE FUNCTION update_location_nameonly(partition INTEGER, OLD_place_id INTEGER, name hstore) RETURNS BOOLEAN +CREATE OR REPLACE FUNCTION update_location_nameonly(partition INTEGER, OLD_place_id BIGINT, name hstore) RETURNS BOOLEAN AS $$ DECLARE newkeywords INTEGER[]; @@ -753,7 +758,7 @@ DECLARE originalnumberrange INTEGER; housenum INTEGER; linegeo GEOMETRY; - search_place_id INTEGER; + search_place_id BIGINT; defpostalcode TEXT; havefirstpoint BOOLEAN; @@ -788,7 +793,7 @@ BEGIN linestr := linestr||','||ST_X(nextnode.geometry)||' '||ST_Y(nextnode.geometry); endnumber := ('0'||substring(nextnode.housenumber,'[0-9]+'))::integer; - IF startnumber IS NOT NULL and startnumber > 0 AND endnumber IS NOT NULL and endnumber > 0 THEN + IF startnumber IS NOT NULL and startnumber > 0 AND endnumber IS NOT NULL and endnumber > 0 AND @(startnumber - endnumber) < 1000 THEN --RAISE WARNING 'interpolation end % % ',nextnode.place_id,endnumber; @@ -870,6 +875,7 @@ DECLARE country_code VARCHAR(2); default_language VARCHAR(10); diameter FLOAT; + classtable TEXT; BEGIN -- RAISE WARNING '%',NEW.osm_id; @@ -980,7 +986,7 @@ BEGIN ELSEIF NEW.type in ('suburb','croft','subdivision') THEN NEW.rank_search := 20; NEW.rank_address := NEW.rank_search; - ELSEIF NEW.type in ('farm','locality','islet') THEN + ELSEIF NEW.type in ('farm','locality','islet','isolated_dwelling') THEN NEW.rank_search := 20; NEW.rank_address := 0; ELSEIF NEW.type in ('hall_of_residence','neighbourhood','housing_estate','nature_reserve') THEN @@ -1143,6 +1149,17 @@ BEGIN END IF; + -- add to tables for special search + -- Note: won't work on initial import because the classtype tables + -- do not yet exist. It won't hurt either. + classtable := 'place_classtype_' || NEW.class || '_' || NEW.type; + SELECT count(*)>0 FROM pg_tables WHERE tablename = classtable INTO result; + IF result THEN + EXECUTE 'INSERT INTO ' || classtable::regclass || ' (place_id, centroid) VALUES ($1,$2)' + USING NEW.place_id, ST_Centroid(NEW.geometry); + END IF; + + -- IF NEW.rank_search < 26 THEN -- RAISE WARNING 'placex insert: % % % %',NEW.osm_type,NEW.osm_id,NEW.class,NEW.type; -- END IF; @@ -1176,7 +1193,7 @@ DECLARE search_maxrank INTEGER; address_maxrank INTEGER; address_street_word_id INTEGER; - parent_place_id_rank INTEGER; + parent_place_id_rank BIGINT; isin TEXT[]; isin_tokens INT[]; @@ -1200,9 +1217,9 @@ BEGIN RETURN NEW; END IF; - -- defered delete + -- deferred delete IF OLD.indexed_status = 100 THEN - delete from placex where osm_type = OLD.osm_type and osm_id = OLD.osm_id and class = OLD.class and type = OLD.type; + delete from placex where place_id = OLD.place_id; RETURN NULL; END IF; @@ -1215,11 +1232,13 @@ BEGIN RETURN NEW; END IF; - result := deleteSearchName(NEW.partition, NEW.place_id); - DELETE FROM place_addressline WHERE place_id = NEW.place_id; - DELETE FROM place_boundingbox where place_id = NEW.place_id; - result := deleteRoad(NEW.partition, NEW.place_id); - result := deleteLocationArea(NEW.partition, NEW.place_id); + IF OLD.indexed_status > 1 THEN + result := deleteSearchName(NEW.partition, NEW.place_id); + DELETE FROM place_addressline WHERE place_id = NEW.place_id; + DELETE FROM place_boundingbox where place_id = NEW.place_id; + result := deleteRoad(NEW.partition, NEW.place_id); + result := deleteLocationArea(NEW.partition, NEW.place_id); + END IF; -- reclaculate country and partition (should probably have a country_code and calculated_country_code as seperate fields) SELECT country_code from place where osm_type = NEW.osm_type and osm_id = NEW.osm_id and class = NEW.class and type = NEW.type INTO NEW.country_code; @@ -1282,7 +1301,7 @@ BEGIN --RAISE WARNING 'x1'; -- Is this node part of a way? - FOR way IN select id from planet_osm_ways where nodes && ARRAY[NEW.osm_id::integer] LOOP + FOR way IN select id from planet_osm_ways where nodes @> ARRAY[NEW.osm_id::integer] LOOP --RAISE WARNING '%', way; FOR location IN select * from placex where osm_type = 'W' and osm_id = way.id LOOP @@ -1294,19 +1313,21 @@ BEGIN END IF; -- Is the WAY part of a relation - FOR relation IN select * from planet_osm_rels where parts @> ARRAY[location.osm_id::integer] and members @> ARRAY['w'||location.osm_id] - LOOP - -- At the moment we only process one type of relation - associatedStreet - IF relation.tags @> ARRAY['associatedStreet'] AND array_upper(relation.members, 1) IS NOT NULL THEN - FOR i IN 1..array_upper(relation.members, 1) BY 2 LOOP - IF NEW.parent_place_id IS NULL AND relation.members[i+1] = 'street' THEN ---RAISE WARNING 'node in way that is in a relation %',relation; - SELECT place_id from placex where osm_type='W' and osm_id = substring(relation.members[i],2,200)::integer - and rank_search = 26 INTO NEW.parent_place_id; + IF NEW.parent_place_id IS NULL THEN + FOR relation IN select * from planet_osm_rels where parts @> ARRAY[location.osm_id::integer] and members @> ARRAY['w'||location.osm_id] + LOOP + -- At the moment we only process one type of relation - associatedStreet + IF relation.tags @> ARRAY['associatedStreet'] AND array_upper(relation.members, 1) IS NOT NULL THEN + FOR i IN 1..array_upper(relation.members, 1) BY 2 LOOP + IF NEW.parent_place_id IS NULL AND relation.members[i+1] = 'street' THEN + --RAISE WARNING 'node in way that is in a relation %',relation; + SELECT place_id from placex where osm_type='W' and osm_id = substring(relation.members[i],2,200)::integer + and rank_search = 26 INTO NEW.parent_place_id; + END IF; + END LOOP; END IF; END LOOP; - END IF; - END LOOP; + END IF; -- If the way contains an explicit name of a street copy it IF NEW.street IS NULL AND location.street IS NOT NULL THEN @@ -1426,31 +1447,29 @@ BEGIN IF array_upper(isin, 1) IS NOT NULL THEN FOR i IN 1..array_upper(isin, 1) LOOP address_street_word_id := get_name_id(make_standard_name(isin[i])); - IF address_street_word_id IS NOT NULL THEN - isin_tokens := isin_tokens + address_street_word_id; + IF address_street_word_id IS NOT NULL AND NOT(ARRAY[address_street_word_id] <@ isin_tokens) THEN + isin_tokens := isin_tokens || address_street_word_id; END IF; END LOOP; END IF; - isin_tokens := uniq(sort(isin_tokens)); END IF; IF NEW.postcode IS NOT NULL THEN isin := regexp_split_to_array(NEW.postcode, E'[;,]'); IF array_upper(isin, 1) IS NOT NULL THEN FOR i IN 1..array_upper(isin, 1) LOOP address_street_word_id := get_name_id(make_standard_name(isin[i])); - IF address_street_word_id IS NOT NULL THEN - isin_tokens := isin_tokens + address_street_word_id; + IF address_street_word_id IS NOT NULL AND NOT(ARRAY[address_street_word_id] <@ isin_tokens) THEN + isin_tokens := isin_tokens || address_street_word_id; END IF; END LOOP; END IF; - isin_tokens := uniq(sort(isin_tokens)); END IF; --RAISE WARNING 'ISIN: %', isin_tokens; -- Process area matches location_rank_search := 100; location_distance := 0; ---RAISE WARNING ' getNearFeatures(%,%,%,%)',NEW.partition, place_centroid, search_maxrank, isin_tokens; +--RAISE WARNING ' getNearFeatures(%,''%'',%,''%'')',NEW.partition, place_centroid, search_maxrank, isin_tokens; FOR location IN SELECT * from getNearFeatures(NEW.partition, place_centroid, search_maxrank, isin_tokens) LOOP --RAISE WARNING ' AREA: %',location; @@ -1460,13 +1479,15 @@ BEGIN location_distance := location.distance * 1.5; END IF; - IF location.distance < location_distance THEN + IF location.distance < location_distance OR NOT location.isguess THEN -- Add it to the list of search terms nameaddress_vector := array_merge(nameaddress_vector, location.keywords::integer[]); INSERT INTO place_addressline VALUES (NEW.place_id, location.place_id, true, NOT address_havelevel[location.rank_address], location.distance, location.rank_address); address_havelevel[location.rank_address] := true; +--RAISE WARNING ' Terms: (%) %',location, nameaddress_vector; + IF location.rank_address > parent_place_id_rank THEN NEW.parent_place_id = location.place_id; parent_place_id_rank = location.rank_address; @@ -1551,6 +1572,7 @@ CREATE OR REPLACE FUNCTION placex_delete() RETURNS TRIGGER AS $$ DECLARE b BOOLEAN; + classtable TEXT; BEGIN IF OLD.rank_address < 30 THEN @@ -1577,6 +1599,13 @@ BEGIN DELETE FROM place_addressline where place_id = OLD.place_id; + -- remove from tables for special search + classtable := 'place_classtype_' || OLD.class || '_' || OLD.type; + SELECT count(*)>0 FROM pg_tables WHERE tablename = classtable INTO b; + IF b THEN + EXECUTE 'DELETE FROM ' || classtable::regclass || ' WHERE place_id = $1' USING OLD.place_id; + END IF; + RETURN OLD; END; @@ -1586,7 +1615,7 @@ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION place_delete() RETURNS TRIGGER AS $$ DECLARE - placeid INTEGER; + placeid BIGINT; BEGIN -- RAISE WARNING 'delete: % % % %',OLD.osm_type,OLD.osm_id,OLD.class,OLD.type; @@ -1614,7 +1643,7 @@ DECLARE existing RECORD; existingplacex RECORD; existinggeometry GEOMETRY; - existingplace_id INTEGER; + existingplace_id BIGINT; result BOOLEAN; partition INTEGER; BEGIN @@ -1653,11 +1682,11 @@ BEGIN select * from placex where osm_type = NEW.osm_type and osm_id = NEW.osm_id and class = NEW.class and type = NEW.type INTO existingplacex; -- Handle a place changing type by removing the old data - -- My generated 'place' types are causing havok because they overlap with real tags + -- My generated 'place' types are causing havok because they overlap with real keys -- TODO: move them to their own special purpose key/class to avoid collisions --- IF existing.osm_type IS NULL AND (NEW.type not in ('postcode','house','houses')) THEN --- DELETE FROM place where osm_type = NEW.osm_type and osm_id = NEW.osm_id and class = NEW.class and type not in ('postcode','house','houses'); --- END IF; + IF existing.osm_type IS NULL AND (NEW.type not in ('postcode','house','houses')) THEN + DELETE FROM place where osm_type = NEW.osm_type and osm_id = NEW.osm_id and class = NEW.class and type not in ('postcode','house','houses'); + END IF; -- RAISE WARNING 'Existing: %',existing.place_id; @@ -1896,7 +1925,9 @@ BEGIN FOR location IN select nodes from planet_osm_ways where id = way_ids[j] LIMIT 1 LOOP - searchnodes := searchnodes | location.nodes; + IF not (ARRAY[location.nodes] <@ searchnodes) THEN + searchnodes := searchnodes || location.nodes; + END IF; END LOOP; END LOOP; @@ -1905,7 +1936,7 @@ END; $$ LANGUAGE plpgsql IMMUTABLE; -CREATE OR REPLACE FUNCTION get_address_postcode(for_place_id INTEGER) RETURNS TEXT +CREATE OR REPLACE FUNCTION get_address_postcode(for_place_id BIGINT) RETURNS TEXT AS $$ DECLARE result TEXT[]; @@ -1930,7 +1961,7 @@ BEGIN IF array_upper(search, 1) IS NOT NULL AND array_upper(location.name, 1) IS NOT NULL THEN FOR j IN 1..array_upper(search, 1) LOOP FOR k IN 1..array_upper(location.name, 1) LOOP - IF (found > location.rank_address AND location.name[k].key = search[j] AND location.name[k].value != '') AND NOT result && ARRAY[trim(location.name[k].value)] AND (for_postcode IS NULL OR location.name[k].value ilike for_postcode||'%') THEN + IF (found > location.rank_address AND location.name[k].key = search[j] AND location.name[k].value != '') AND NOT result @> ARRAY[trim(location.name[k].value)] AND (for_postcode IS NULL OR location.name[k].value ilike for_postcode||'%') THEN result[(100 - location.rank_address)] := trim(location.name[k].value); found := location.rank_address; END IF; @@ -1944,7 +1975,7 @@ END; $$ LANGUAGE plpgsql; -CREATE OR REPLACE FUNCTION get_address_by_language(for_place_id INTEGER, languagepref TEXT[]) RETURNS TEXT +CREATE OR REPLACE FUNCTION get_address_by_language(for_place_id BIGINT, languagepref TEXT[]) RETURNS TEXT AS $$ DECLARE result TEXT[]; @@ -1971,7 +2002,7 @@ LANGUAGE plpgsql; DROP TYPE addressline CASCADE; create type addressline as ( - place_id INTEGER, + place_id BIGINT, osm_type CHAR(1), osm_id INTEGER, name HSTORE, @@ -1984,10 +2015,10 @@ create type addressline as ( distance FLOAT ); -CREATE OR REPLACE FUNCTION get_addressdata(in_place_id INTEGER) RETURNS setof addressline +CREATE OR REPLACE FUNCTION get_addressdata(in_place_id BIGINT) RETURNS setof addressline AS $$ DECLARE - for_place_id INTEGER; + for_place_id BIGINT; result TEXT[]; search TEXT[]; found INTEGER; @@ -2135,7 +2166,7 @@ END; $$ LANGUAGE plpgsql; -CREATE OR REPLACE FUNCTION get_place_boundingbox(search_place_id INTEGER) RETURNS place_boundingbox +CREATE OR REPLACE FUNCTION get_place_boundingbox(search_place_id BIGINT) RETURNS place_boundingbox AS $$ DECLARE result place_boundingbox; @@ -2172,7 +2203,7 @@ $$ LANGUAGE plpgsql; -- don't do the operation if it would be slow -CREATE OR REPLACE FUNCTION get_place_boundingbox_quick(search_place_id INTEGER) RETURNS place_boundingbox +CREATE OR REPLACE FUNCTION get_place_boundingbox_quick(search_place_id BIGINT) RETURNS place_boundingbox AS $$ DECLARE result place_boundingbox; @@ -2211,7 +2242,7 @@ END; $$ LANGUAGE plpgsql; -CREATE OR REPLACE FUNCTION update_place(search_place_id INTEGER) RETURNS BOOLEAN +CREATE OR REPLACE FUNCTION update_place(search_place_id BIGINT) RETURNS BOOLEAN AS $$ DECLARE result place_boundingbox; @@ -2384,7 +2415,7 @@ DECLARE rangestartnumber INTEGER; place_centroid GEOMETRY; partition INTEGER; - parent_place_id INTEGER; + parent_place_id BIGINT; location RECORD; address_street_word_id INTEGER; @@ -2464,32 +2495,41 @@ DECLARE newpoints INTEGER; place_centroid GEOMETRY; partition INTEGER; - parent_place_id INTEGER; + out_parent_place_id BIGINT; location RECORD; address_street_word_id INTEGER; + out_postcode TEXT; BEGIN place_centroid := ST_Centroid(pointgeo); partition := get_partition(place_centroid, in_countrycode); - parent_place_id := null; + out_parent_place_id := null; address_street_word_id := get_name_id(make_standard_name(in_street)); IF address_street_word_id IS NOT NULL THEN FOR location IN SELECT * from getNearestNamedRoadFeature(partition, place_centroid, address_street_word_id) LOOP - parent_place_id := location.place_id; + out_parent_place_id := location.place_id; END LOOP; END IF; - IF parent_place_id IS NULL THEN + IF out_parent_place_id IS NULL THEN FOR location IN SELECT place_id FROM getNearestRoadFeature(partition, place_centroid) LOOP - parent_place_id := location.place_id; + out_parent_place_id := location.place_id; END LOOP; END IF; + out_postcode := in_postcode; + IF out_postcode IS NULL THEN + SELECT postcode from placex where place_id = out_parent_place_id INTO out_postcode; + END IF; + IF out_postcode IS NULL THEN + out_postcode := getNearestPostcode(partition, place_centroid); + END IF; + newpoints := 0; insert into location_property_aux (place_id, partition, parent_place_id, housenumber, postcode, centroid) - values (nextval('seq_place'), partition, parent_place_id, in_housenumber, in_postcode, place_centroid); + values (nextval('seq_place'), partition, out_parent_place_id, in_housenumber, out_postcode, place_centroid); newpoints := newpoints + 1; RETURN newpoints;