J'ai besoin d'un conseil pour rechercher un enregistrement en fonction de la chaîne spécifiée.
Les chaînes de recherche peuvent contenir des valeurs de ces colonnes. Les valeurs de cette chaîne ne doivent pas être strictement identiques, dans le bon ordre, et les valeurs de certaines colonnes de cette chaîne peuvent également être manquantes.
Exemple de chaîne de recherche:
22 Karntner Wien
Et j'obtiens par exemple le résultat avec les 5 meilleurs enregistrements similaires.
Je pense que je devrais utiliser la recherche en texte intégral, mais je n'ai aucune expérience avec cela. Pouvez-vous me dire comment procéder?
Je suggère cette expression pour la requête et l'index:
SELECT * FROM tbl
WHERE to_tsvector('simple', f_concat_ws(' ', country, city, street, house_nr, postcode))
@@ plainto_tsquery('simple', '22 Kärntner Wien');
Notez la fonction personnalisée f_concat_ws()
ci-dessus. C'est parce que concat_ws()
n'est que STABLE
pas IMMUTABLE
. Vous devez le créer d'abord:
CREATE OR REPLACE FUNCTION f_concat_ws(text, VARIADIC text[])
RETURNS text LANGUAGE sql IMMUTABLE AS 'SELECT array_to_string($2, $1)';
Il peut être utilisé comme remplacement direct pour concat_ws()
, sauf qu'il n'accepte que les données de texte réelles en entrée (ce qui nous permet de les faire IMMUTABLE
sans tricher , effectivement). Explication détaillée (lisez-la!):
À propos de VARIADIC
:
Pour beaucoup colonnes, c'est plus court et plus rapide. Vous pourriez vous en passer mais la syntaxe devient plutôt verbeuse (voir réponse de joanolo ).
L'index correspondant pour aller avec ceci:
CREATE INDEX tbl_adr_fts_idx ON tbl USING GIN (
to_tsvector('simple', f_concat_ws(' ', country, city, street, house_nr, postcode)));
Vous traitez avec des données d'adresse internationales, alors faites pas utilisez la configuration de recherche de texte english
. La racine a peu de sens pour names et la plupart de vos exemples de données ne sont même pas en anglais pour commencer. Utilisez plutôt la configuration simple
. Vous avez besoin du formulaire avec deux paramètres - voir ci-dessous.
Concatène les chaînes et appelle la fonction la plus chère to_tsvector()
une fois. Utilisez concat_ws()
pour gérer avec élégance les éventuelles valeurs NULL. Moins cher dans l'ensemble et aussi plus court.
Comme je l'ai commenté, la recherche en texte intégral a un support limité pour la correspondance floue, mais il y a la fonctionnalité souvent négligée de la correspondance de préfixe:
Donc, si vous ne savez pas si c'est 'Kärntner' ou 'Kärnten', et si c'est 'Straße' , 'strasse' ou 'Strabe' (comme dans vos données d'exemple de buggy) mais vous savez que le deuxième Word suit le premier, vous pourriez:
... @@ to_tsquery('simple', '22 & Kärnt:* <-> Stra:* & Wien')
<->
Est l'opérateur de recherche de phrases et nécessite Postgres 9.6 .
Et si vous voulez également ignorer les signes diacritiques ( 'ä' <> 'a'), ajoutez unaccent()
au mix . Vous pouvez l'utiliser séparément fonction ou vous pouvez l'ajouter comme dictionnaire dans votre configuration de recherche de texte. Vous devez d'abord installer l'extension ...
Aperçu de l'option de correspondance de motifs dans les installations Postgres typiques:
J'essaye d'ajouter cet index mais il me donne une erreur:
ERROR: functions in index expression must be marked IMMUTABLE
Il existe deux variantes de la fonction to_tsvector()
- voir "surcharge de fonction ". Le 1er prend seulement text
, le 2e prend regconfig
et text
. Voir par vous-même:
SELECT proname, provolatile, proargtypes[0]::regtype, proargtypes[1]::regtype
FROM pg_proc
WHERE proname = 'to_tsvector';
Seul le second est IMMUTABLE
et peut être utilisé directement dans une expression d'index. 'simple' dans l'exemple ci-dessus est une configuration de recherche de texte (regconfig
).
Plus important encore , ma surveillance: concat_ws()
(que j'avais dans ma première version) est seulement STABLE
, pas IMMUTABLE
. J'ai ajouté les étapes nécessaires ci-dessus.
En relation:
Imaginons que ce soit votre table et quelques données:
CREATE TABLE t
(
country text,
city text,
street text,
house_number text,
post_code text
) ;
INSERT INTO t
VALUES
('Österreich', 'Vienna', 'HauptStrasse', '123', '12345'),
('France', 'Paris', 'Rue du Midi', '12A', '01234'),
('España', 'Barcelona', 'Passeig de Gràcia', '32', '08001'),
('United Kingdom', 'London', 'Oxford Street', '20', 'W1D 1AS'),
('Nederland', 'Amsterdam', 'Leidsekruisstraat', '6-8', '1017 RH') ;
[REMARQUE: vérifiez-le à http://rextester.com/DOJN8533]
La façon d'effectuer une recherche en texte intégral sur plusieurs colonnes à l'aide de PostgreSQL (en supposant que "anglais" est le nom de votre configuration FTS), consiste à utiliser une requête comme:
SELECT
*
FROM
t
WHERE
(
to_tsvector('english', coalesce(country, '')) ||
to_tsvector('english', coalesce(city, '')) ||
to_tsvector('english', coalesce(street, '')) ||
to_tsvector('english', coalesce(house_number, '')) ||
to_tsvector('english', coalesce(post_code, ''))
) @@ plainto_tsquery('english', 'Amsterdam') ;
Le où clasuse signifie:
(this tsvector = document) @@ /* matches */ (this tsquery = query)
Un tsvector
est un type de données spécial utilisé par PostgreSQL pour stocker des données transformées (par exemple, toutes en minuscules; avec des virgules supprimées, avec des mots identifiés et répertoriés, etc.) concernant un texte. Un tsquery
est un moyen de demander les caractéristiques d'un document (par exemple contenant ce _et_ cela).
Le ||
L'opérateur combine les tsvecteurs (disons qu'il "les additionne").
Si vous voulez accélérer les choses, vous devez avoir un index fonctionnel, défini comme:
CREATE INDEX ts_idx
ON t USING Gist (
(
to_tsvector('english', coalesce(country, '')) ||
to_tsvector('english', coalesce(city, '')) ||
to_tsvector('english', coalesce(street, '')) ||
to_tsvector('english', coalesce(house_number, '')) ||
to_tsvector('english', coalesce(post_code, ''))
)
) ;
Vous devez vérifier attentivement la documentation sur Recherche plein texte . C'est un peu intimidant, car il y a beaucoup de possibilités, mais cela vaut la peine de passer du temps.
Pour trier les résultats lorsqu'ils sont nombreux, vous devez utiliser he ts_rank
fonction à ORDER BY
puis limitez.