Skip to content

Latest commit

 

History

History
1329 lines (1055 loc) · 34.8 KB

File metadata and controls

1329 lines (1055 loc) · 34.8 KB

🔝 Retour au Sommaire

18.3.3. Dictionnaires et Langues Multiples

Introduction

Imaginez que vous construisez une plateforme internationale qui doit supporter la recherche en français, anglais, espagnol et arabe simultanément. Ou que vous devez personnaliser le comportement de la recherche pour votre domaine spécifique (médical, juridique, technique).

Les dictionnaires et les configurations linguistiques de PostgreSQL vous permettent de :

  • Gérer correctement le stemming (réduction des mots) dans différentes langues
  • Définir des stop words (mots vides) personnalisés
  • Créer des synonymes et des thésaurus
  • Supporter le multilinguisme dans une même application
  • Adapter le traitement du texte à votre domaine métier

Sans configuration linguistique :

-- Recherche sans stemming français
SELECT to_tsvector('simple', 'Les bases de données sont essentielles');
-- Résultat : 'bases':2 'de':3 'données':4 'essentielles':6 'les':1 'sont':5
-- Problème : "Les", "de", "sont" ne sont pas filtrés

Avec configuration française :

-- Recherche avec stemming français
SELECT to_tsvector('french', 'Les bases de données sont essentielles');
-- Résultat : 'base':2 'donn':4 'essenti':6
-- Stop words supprimés, stemming appliqué

1. Architecture des Dictionnaires PostgreSQL

Les Composants du Full-Text Search

Le traitement d'un texte en Full-Text Search suit ce pipeline :

Texte original
     ↓
[1. PARSER] → Découpage en tokens (mots, nombres, ponctuation)
     ↓
[2. DICTIONNAIRES] → Normalisation, stemming, filtrage
     ↓
[3. TSVECTOR] → Représentation finale indexable

Les Trois Niveaux

1. Parser (Analyseur)

Le parser découpe le texte en tokens (unités lexicales).

-- Voir les tokens produits par le parser
SELECT * FROM ts_token_type('default');

Résultat :

tokid | alias      | description
------+------------+---------------------------
    1 | asciiword  | Word, all ASCII
    2 | word       | Word, all letters
    3 | numword    | Word, letters and digits
    4 | email      | Email address
    5 | url        | URL
    6 | host       | Host
    7 | sfloat     | Scientific notation
    8 | version    | Version number
   12 | blank      | Space symbols
   23 | tag        | XML tag

Exemple de parsing :

SELECT * FROM ts_parse('default', 'PostgreSQL 16.2 est disponible sur postgresql.org');

Résultat :

tokid |    token
------+-------------
    1 | PostgreSQL
   12 |
    8 | 16.2
   12 |
    1 | est
   12 |
    1 | disponible
   12 |
    1 | sur
   12 |
    6 | postgresql.org

2. Dictionnaires

Les dictionnaires transforment les tokens en lexèmes (formes normalisées).

Types de dictionnaires :

  • Stop words : Filtre les mots vides
  • Stemming : Réduit les mots à leur racine
  • Synonym : Remplace par des synonymes
  • Thesaurus : Gestion avancée des synonymes
  • Ispell : Dictionnaires morphologiques
  • Snowball : Algorithmes de stemming

3. Configuration (Text Search Configuration)

La configuration lie les types de tokens aux dictionnaires appropriés.

-- Voir les configurations disponibles
SELECT cfgname FROM pg_ts_config;
-- Résultat : simple, danish, dutch, english, finnish, french, german, etc.

2. Configurations Linguistiques Pré-définies

Configurations Disponibles

PostgreSQL offre 20+ configurations linguistiques prêtes à l'emploi :

-- Lister toutes les configurations avec détails
SELECT
    cfgname AS nom,
    cfgnamespace::regnamespace AS schema,
    (SELECT COUNT(*) FROM pg_ts_config_map WHERE mapcfg = c.oid) AS nombre_mappings
FROM pg_ts_config c  
ORDER BY cfgname;  

Principales configurations :

Configuration Langue Stemmer Stop Words
simple Aucune ❌ Non ❌ Non
english Anglais ✅ Snowball ✅ Oui
french Français ✅ Snowball ✅ Oui
spanish Espagnol ✅ Snowball ✅ Oui
german Allemand ✅ Snowball ✅ Oui
italian Italien ✅ Snowball ✅ Oui
portuguese Portugais ✅ Snowball ✅ Oui
russian Russe ✅ Snowball ✅ Oui
arabic Arabe ✅ Oui ✅ Oui
danish Danois ✅ Snowball ✅ Oui
dutch Néerlandais ✅ Snowball ✅ Oui
finnish Finnois ✅ Snowball ✅ Oui
hungarian Hongrois ✅ Snowball ✅ Oui
norwegian Norvégien ✅ Snowball ✅ Oui
romanian Roumain ✅ Snowball ✅ Oui
swedish Suédois ✅ Snowball ✅ Oui
turkish Turc ✅ Snowball ✅ Oui

Comparaison des Configurations

-- Comparer le traitement d'un même texte dans différentes langues
SELECT
    'simple' AS config,
    to_tsvector('simple', 'running runs runner') AS result
UNION ALL  
SELECT  
    'english',
    to_tsvector('english', 'running runs runner')
UNION ALL  
SELECT  
    'french',
    to_tsvector('french', 'courir cours coureur');

Résultat :

config  | result
--------+----------------------------
simple  | 'runner':3 'running':1 'runs':2  
english | 'run':1,2,3                    ← Stemming : tous → "run"  
french  | 'cour':1,2,3                   ← Stemming : tous → "cour"  

Configuration par Défaut

-- Voir la configuration par défaut
SHOW default_text_search_config;
-- Résultat typique : pg_catalog.english (ou french selon l'installation)

-- Changer la configuration par défaut pour la session
SET default_text_search_config = 'french';

-- Utiliser to_tsvector sans spécifier la langue (utilise le défaut)
SELECT to_tsvector('Les bases de données');
-- Utilise maintenant la configuration française

3. Anatomie d'une Configuration

Inspecter une Configuration

-- Détails de la configuration française
SELECT
    cfgname,
    (SELECT dictname FROM pg_ts_dict d
     WHERE d.oid = ANY(m.mapdict) LIMIT 1) AS premier_dictionnaire
FROM pg_ts_config c  
JOIN pg_ts_config_map m ON m.mapcfg = c.oid  
WHERE cfgname = 'french'  
LIMIT 5;  

Mapping Token → Dictionnaires

Chaque configuration associe des types de tokens à des dictionnaires :

-- Voir le mapping complet pour le français
SELECT
    tt.alias AS type_token,
    tt.description,
    ARRAY_AGG(d.dictname ORDER BY m.mapseqno) AS dictionnaires
FROM pg_ts_config c  
JOIN pg_ts_config_map m ON m.mapcfg = c.oid  
JOIN pg_ts_dict d ON d.oid = ANY(m.mapdict)  
JOIN ts_token_type(c.cfgparser) tt ON tt.tokid = m.maptokentype  
WHERE c.cfgname = 'french'  
GROUP BY tt.alias, tt.description, m.maptokentype  
ORDER BY m.maptokentype;  

Résultat typique pour "french" :

type_token  | description         | dictionnaires
------------+--------------------+------------------------
asciiword   | Word, all ASCII    | {french_stem}  
word        | Word, all letters  | {french_stem}  
numword     | Word, letters+num  | {simple}  
email       | Email address      | {simple}  
url         | URL                | {simple}  
host        | Host               | {simple}  

Interprétation :

  • Les mots (asciiword, word) passent par french_stem (stemming français)
  • Les nombres et URLs passent par simple (pas de transformation)
  • Les espaces et ponctuation sont ignorés

Ordre d'Application des Dictionnaires

Les dictionnaires sont appliqués séquentiellement jusqu'à ce que l'un produise un résultat :

Token: "couraient"
   ↓
[1] french_stem → "cour" ✓ (succès, on s'arrête)
   ↓
Lexème: "cour"

Si le premier dictionnaire ne produit rien (stop word), le suivant est essayé :

Token: "le"
   ↓
[1] french_stem → NULL (stop word, aucun résultat)
   ↓
[2] simple → NULL (pas d'autre dictionnaire)
   ↓
Lexème: (aucun, token ignoré)

4. Les Types de Dictionnaires

4.1. Simple Dictionary

Le dictionnaire simple ne fait aucune transformation : il retourne le token tel quel en minuscules.

-- Créer un dictionnaire simple
CREATE TEXT SEARCH DICTIONARY public.simple_dict (
    TEMPLATE = pg_catalog.simple
);

-- Test
SELECT ts_lexize('simple_dict', 'PostgreSQL');
-- Résultat : {postgresql}

SELECT ts_lexize('simple_dict', 'Les');
-- Résultat : {les} (même les stop words sont conservés)

Usage : Pour les tokens qu'on ne veut pas transformer (URLs, emails, codes).

4.2. Snowball Dictionary (Stemming)

Le dictionnaire Snowball applique un algorithme de stemming pour réduire les mots à leur racine.

-- Voir les stemmers Snowball disponibles
SELECT * FROM pg_ts_dict WHERE dictname LIKE '%stem';

Algorithmes Snowball :

  • danish_stem : Danois
  • dutch_stem : Néerlandais
  • english_stem : Anglais
  • finnish_stem : Finnois
  • french_stem : Français
  • german_stem : Allemand
  • hungarian_stem : Hongrois
  • italian_stem : Italien
  • norwegian_stem : Norvégien
  • portuguese_stem : Portugais
  • romanian_stem : Roumain
  • russian_stem : Russe
  • spanish_stem : Espagnol
  • swedish_stem : Suédois
  • turkish_stem : Turc

Exemple de stemming français :

-- Tester le stemmer français
SELECT ts_lexize('french_stem', 'couraient');
-- Résultat : {cour}

SELECT ts_lexize('french_stem', 'coureur');
-- Résultat : {cour}

SELECT ts_lexize('french_stem', 'courses');
-- Résultat : {cours}

SELECT ts_lexize('french_stem', 'bases');
-- Résultat : {base}

SELECT ts_lexize('french_stem', 'données');
-- Résultat : {donn}

Créer un dictionnaire Snowball personnalisé :

-- Créer un stemmer français avec stop words personnalisés
CREATE TEXT SEARCH DICTIONARY french_custom (
    TEMPLATE = snowball,
    Language = french,
    StopWords = french  -- Fichier de stop words (optionnel)
);

4.3. Ispell Dictionary (Dictionnaires Morphologiques)

Les dictionnaires Ispell utilisent des fichiers de dictionnaires morphologiques (comme les correcteurs orthographiques).

-- Créer un dictionnaire Ispell pour le français
CREATE TEXT SEARCH DICTIONARY french_ispell (
    TEMPLATE = ispell,
    DictFile = french,    -- Fichier .dict
    AffFile = french,     -- Fichier .affix
    StopWords = french    -- Fichier de stop words
);

-- Les fichiers doivent être dans $SHAREDIR/tsearch_data/
-- Exemple : french.dict, french.affix

Avantages Ispell :

  • Plus précis que Snowball (connaissance morphologique)
  • Gère les irrégularités linguistiques
  • Peut faire de la lemmatisation (pas juste du stemming)

Inconvénients :

  • Nécessite des fichiers de dictionnaires externes
  • Plus lent que Snowball
  • Fichiers volumineux

Exemple d'utilisation :

-- Si le dictionnaire est configuré
SELECT ts_lexize('french_ispell', 'couraient');
-- Résultat : {courir} (lemme, pas juste stem)

4.4. Synonym Dictionary (Synonymes)

Le dictionnaire synonym remplace des mots par leurs synonymes.

-- Créer un fichier de synonymes : /usr/share/postgresql/16/tsearch_data/synonyms.syn
-- Contenu du fichier :
-- postgres    postgresql
-- pgsql       postgresql
-- pg          postgresql

-- Créer le dictionnaire
CREATE TEXT SEARCH DICTIONARY synonym_dict (
    TEMPLATE = synonym,
    SYNONYMS = synonyms  -- Nom du fichier (sans .syn)
);

-- Test
SELECT ts_lexize('synonym_dict', 'postgres');
-- Résultat : {postgresql}

SELECT ts_lexize('synonym_dict', 'pgsql');
-- Résultat : {postgresql}

Format du fichier de synonymes :

# Commentaire
mot_original    synonyme

# Exemples
db              database  
rdbms           database  
sgbd            database  
bdd             database  

4.5. Thesaurus Dictionary (Thésaurus)

Le dictionnaire thesaurus est une version avancée du dictionnaire de synonymes qui gère des phrases multi-mots.

-- Fichier thesaurus.ths
-- Format : phrase originale : liste de synonymes
-- Exemple :
-- postgres sql : postgresql
-- relational database : rdbms
-- artificial intelligence : ai machine_learning

-- Créer le dictionnaire
CREATE TEXT SEARCH DICTIONARY thesaurus_dict (
    TEMPLATE = thesaurus,
    DictFile = thesaurus,      -- Fichier .ths
    Dictionary = english_stem  -- Dictionnaire sous-jacent pour stemming
);

-- Test
SELECT ts_lexize('thesaurus_dict', 'relational');
-- Résultat : NULL (attente du mot suivant "database")

-- Utilisation via to_tsvector (traite les phrases)
SELECT to_tsvector('thesaurus_config', 'relational database system');
-- Résultat : 'rdbms':1 'system':3

5. Stop Words (Mots Vides)

Qu'est-ce qu'un Stop Word ?

Les stop words sont des mots très fréquents et peu significatifs qu'on ignore lors de l'indexation.

Exemples français : le, la, les, un, une, de, du, des, à, au, en, et, ou, mais, etc.
Exemples anglais : the, a, an, in, on, at, to, for, of, with, etc.

Fichiers de Stop Words

Les configurations linguistiques utilisent des fichiers de stop words :

# Localisation typique
/usr/share/postgresql/16/tsearch_data/

# Fichiers
french.stop      # Stop words français  
english.stop     # Stop words anglais  
spanish.stop     # Stop words espagnol  
# etc.

Voir les Stop Words

-- Tester si un mot est un stop word
SELECT ts_lexize('french_stem', 'le');
-- Résultat : NULL (c'est un stop word)

SELECT ts_lexize('french_stem', 'les');
-- Résultat : NULL (stop word)

SELECT ts_lexize('french_stem', 'postgresql');
-- Résultat : {postgresql} (pas un stop word)

Liste des Stop Words Français

Extrait du fichier french.stop :

au  
aux  
avec  
ce  
ces  
dans  
de  
des  
du  
elle  
en  
et  
eux  
il  
je  
la  
le  
leur  
lui  
ma  
mais  
me  
même  
mes  
moi  
mon  
ne  
nos  
notre  
nous  
on  
ou  
par  
pas  
pour  
qu  
que  
qui  
sa  
se  
ses  
son  
sur  
ta  
te  
tes  
toi  
ton  
tu  
un  
une  
vos  
votre  
vous  

Créer un Fichier de Stop Words Personnalisé

-- 1. Créer le fichier /usr/share/postgresql/16/tsearch_data/custom_french.stop
-- Contenu : un mot par ligne
-- Exemple :
-- le
-- la
-- les
-- mon_mot_specifique
-- acronyme_frequent

-- 2. Créer un dictionnaire avec ces stop words
CREATE TEXT SEARCH DICTIONARY french_custom_stop (
    TEMPLATE = snowball,
    Language = french,
    StopWords = custom_french  -- Sans l'extension .stop
);

-- 3. L'utiliser dans une configuration

6. Créer une Configuration Personnalisée

Pourquoi Créer une Configuration ?

  • Combiner plusieurs dictionnaires
  • Personnaliser le traitement pour votre domaine
  • Gérer des cas spécifiques (termes techniques, acronymes)
  • Optimiser pour votre application

Étapes de Création

1. Créer les Dictionnaires Nécessaires

-- Dictionnaire de synonymes techniques
CREATE TEXT SEARCH DICTIONARY tech_synonyms (
    TEMPLATE = synonym,
    SYNONYMS = tech_synonyms  -- Fichier tech_synonyms.syn
);

-- Dictionnaire de stemming français
-- (déjà existant : french_stem)

-- Dictionnaire simple pour les codes/acronymes
CREATE TEXT SEARCH DICTIONARY simple_dict (
    TEMPLATE = pg_catalog.simple
);

2. Créer la Configuration

-- Créer une nouvelle configuration basée sur french
CREATE TEXT SEARCH CONFIGURATION french_tech (
    COPY = french  -- Copie la configuration française
);

-- Ou créer une configuration from scratch
CREATE TEXT SEARCH CONFIGURATION custom_config (
    PARSER = default
);

3. Modifier les Mappings

-- Ajouter le dictionnaire de synonymes AVANT le stemmer
ALTER TEXT SEARCH CONFIGURATION french_tech
    ALTER MAPPING FOR asciiword, word
    WITH tech_synonyms, french_stem;

-- Les tokens "asciiword" et "word" passeront par:
-- 1. tech_synonyms (remplace par synonyme si trouvé)
-- 2. french_stem (stemming si pas de synonyme)

4. Utiliser la Configuration

-- Test
SELECT to_tsvector('french_tech', 'Le postgresql et le pgsql');
-- Si tech_synonyms.syn contient : pgsql postgresql
-- Résultat : 'postgresql':2,5 (pgsql remplacé par postgresql)

-- Utiliser dans une table
ALTER TABLE articles  
ADD COLUMN search_vector tsvector;  

UPDATE articles  
SET search_vector = to_tsvector('french_tech', titre || ' ' || contenu);  

Exemple Complet : Configuration E-commerce

-- 1. Dictionnaire de synonymes produits
-- Fichier : /tsearch_data/ecommerce_synonyms.syn
-- Contenu :
-- smartphone    telephone
-- portable      telephone
-- tel           telephone
-- ordi          ordinateur
-- pc            ordinateur

CREATE TEXT SEARCH DICTIONARY ecommerce_synonyms (
    TEMPLATE = synonym,
    SYNONYMS = ecommerce_synonyms
);

-- 2. Stop words personnalisés (ignorer marques communes)
-- Fichier : /tsearch_data/ecommerce.stop
-- Contenu : article, produit, neuf, occasion, promo

CREATE TEXT SEARCH DICTIONARY ecommerce_stop (
    TEMPLATE = snowball,
    Language = french,
    StopWords = ecommerce
);

-- 3. Créer la configuration
CREATE TEXT SEARCH CONFIGURATION french_ecommerce (COPY = french);

-- 4. Configurer les mappings
ALTER TEXT SEARCH CONFIGURATION french_ecommerce
    ALTER MAPPING FOR asciiword, word
    WITH ecommerce_synonyms, ecommerce_stop;

-- 5. Test
SELECT to_tsvector('french_ecommerce', 'smartphone portable Apple neuf');
-- Résultat : 'appl':3 'telephon':1,2
-- "smartphone" et "portable" → "telephon"
-- "neuf" → ignoré (stop word)
-- "Apple" → "appl" (stemming)

7. Multilinguisme : Gérer Plusieurs Langues

Approche 1 : Colonne de Langue + Indexation Conditionnelle

-- Table multilingue
CREATE TABLE articles (
    id SERIAL PRIMARY KEY,
    titre VARCHAR(200),
    contenu TEXT,
    langue VARCHAR(10),  -- 'fr', 'en', 'es', etc.
    search_vector tsvector
);

-- Trigger intelligent qui choisit la configuration selon la langue
CREATE OR REPLACE FUNCTION articles_multilingual_trigger()  
RETURNS trigger AS $$  
DECLARE  
    config regconfig;
BEGIN
    -- Déterminer la configuration selon la langue
    config := CASE NEW.langue
        WHEN 'fr' THEN 'french'::regconfig
        WHEN 'en' THEN 'english'::regconfig
        WHEN 'es' THEN 'spanish'::regconfig
        WHEN 'de' THEN 'german'::regconfig
        WHEN 'it' THEN 'italian'::regconfig
        ELSE 'simple'::regconfig  -- Fallback
    END;

    -- Créer le tsvector avec la bonne configuration
    NEW.search_vector := to_tsvector(config, COALESCE(NEW.titre, '') || ' ' || COALESCE(NEW.contenu, ''));

    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER tsvector_update  
BEFORE INSERT OR UPDATE ON articles  
FOR EACH ROW EXECUTE FUNCTION articles_multilingual_trigger();  

-- Index
CREATE INDEX idx_articles_search ON articles USING GIN(search_vector);

-- Insertion
INSERT INTO articles (titre, contenu, langue) VALUES
    ('Guide PostgreSQL', 'PostgreSQL est puissant', 'fr'),
    ('PostgreSQL Guide', 'PostgreSQL is powerful', 'en'),
    ('Guía PostgreSQL', 'PostgreSQL es potente', 'es');

-- Recherche multilingue
SELECT titre, langue  
FROM articles  
WHERE search_vector @@ to_tsquery('french', 'puissant')  
   OR search_vector @@ to_tsquery('english', 'powerful')
   OR search_vector @@ to_tsquery('spanish', 'potente');

Approche 2 : Colonnes Séparées par Langue

-- Table avec colonnes par langue
CREATE TABLE produits (
    id SERIAL PRIMARY KEY,
    nom_fr VARCHAR(200),
    description_fr TEXT,
    nom_en VARCHAR(200),
    description_en TEXT,
    nom_es VARCHAR(200),
    description_es TEXT,
    search_vector_fr tsvector,
    search_vector_en tsvector,
    search_vector_es tsvector
);

-- Triggers séparés pour chaque langue
CREATE OR REPLACE FUNCTION produits_fr_trigger() RETURNS trigger AS $$  
BEGIN  
    NEW.search_vector_fr := to_tsvector('french',
        COALESCE(NEW.nom_fr, '') || ' ' || COALESCE(NEW.description_fr, '')
    );
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER tsvector_fr_update  
BEFORE INSERT OR UPDATE ON produits  
FOR EACH ROW EXECUTE FUNCTION produits_fr_trigger();  

-- Répéter pour EN et ES...

-- Index séparés
CREATE INDEX idx_produits_search_fr ON produits USING GIN(search_vector_fr);  
CREATE INDEX idx_produits_search_en ON produits USING GIN(search_vector_en);  
CREATE INDEX idx_produits_search_es ON produits USING GIN(search_vector_es);  

-- Recherche par langue
SELECT nom_fr, description_fr  
FROM produits  
WHERE search_vector_fr @@ plainto_tsquery('french', 'chaussure rouge');  

Approche 3 : Configuration Multilingue Unifiée

Pour des cas simples où on veut indexer dans plusieurs langues simultanément :

-- Créer une configuration qui combine plusieurs langues
CREATE TEXT SEARCH CONFIGURATION multilang (PARSER = default);

-- Mapper les tokens vers plusieurs stemmers
ALTER TEXT SEARCH CONFIGURATION multilang
    ADD MAPPING FOR asciiword, word
    WITH french_stem, english_stem, spanish_stem;

-- Utilisation
SELECT to_tsvector('multilang', 'running courir corriendo');
-- Résultat : 'cour':2 'corr':3 'run':1
-- (chaque mot est stemmed par son stemmer approprié)

⚠️ Attention : Cette approche est moins précise car elle ne connaît pas la langue de chaque mot.

Approche 4 : Détection Automatique de Langue

-- Fonction de détection basique (simplifié)
CREATE OR REPLACE FUNCTION detect_language(text TEXT)  
RETURNS regconfig AS $$  
DECLARE  
    config regconfig;
BEGIN
    -- Détection basique par mots communs
    IF text ~* '\m(the|is|are|and)\M' THEN
        config := 'english';
    ELSIF text ~* '\m(le|la|les|est|sont|et)\M' THEN
        config := 'french';
    ELSIF text ~* '\m(el|la|los|las|es|son|y)\M' THEN
        config := 'spanish';
    ELSE
        config := 'simple';
    END IF;

    RETURN config;
END;
$$ LANGUAGE plpgsql IMMUTABLE;

-- Utilisation dans un trigger
CREATE OR REPLACE FUNCTION articles_auto_lang_trigger() RETURNS trigger AS $$  
BEGIN  
    NEW.search_vector := to_tsvector(
        detect_language(NEW.contenu),
        COALESCE(NEW.titre, '') || ' ' || COALESCE(NEW.contenu, '')
    );
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

⚠️ Note : La détection de langue est complexe. Pour des besoins sérieux, utilisez une bibliothèque externe (langdetect, polyglot) ou une colonne de langue explicite.


8. Cas d'Usage Avancés

Cas 1 : Documentation Technique avec Acronymes

-- Dictionnaire de préservation d'acronymes
-- Fichier : tech_preserve.syn
-- Contenu :
-- sql     SQL
-- html    HTML
-- css     CSS
-- api     API
-- json    JSON
-- xml     XML

CREATE TEXT SEARCH DICTIONARY tech_preserve (
    TEMPLATE = synonym,
    SYNONYMS = tech_preserve
);

-- Configuration technique
CREATE TEXT SEARCH CONFIGURATION tech_doc (COPY = english);

ALTER TEXT SEARCH CONFIGURATION tech_doc
    ALTER MAPPING FOR asciiword
    WITH tech_preserve, english_stem;

-- Test
SELECT to_tsvector('tech_doc', 'This API uses JSON and SQL');
-- Résultat : 'API':2 'JSON':4 'SQL':6 'use':3
-- Acronymes préservés en majuscules

Cas 2 : Contenu Médical avec Termes Spécialisés

-- Synonymes médicaux
-- Fichier : medical_synonyms.syn
-- Contenu :
-- imc             indice_masse_corporelle
-- avc             accident_vasculaire_cerebral
-- ecg             electrocardiogramme
-- irm             imagerie_resonance_magnetique

CREATE TEXT SEARCH DICTIONARY medical_synonyms (
    TEMPLATE = synonym,
    SYNONYMS = medical_synonyms
);

-- Stop words médicaux (mots trop fréquents)
-- Fichier : medical.stop
-- Contenu : patient, traitement, diagnostic, symptome

CREATE TEXT SEARCH DICTIONARY medical_stem (
    TEMPLATE = snowball,
    Language = french,
    StopWords = medical
);

-- Configuration médicale
CREATE TEXT SEARCH CONFIGURATION french_medical (COPY = french);

ALTER TEXT SEARCH CONFIGURATION french_medical
    ALTER MAPPING FOR asciiword, word
    WITH medical_synonyms, medical_stem;

-- Test
SELECT to_tsvector('french_medical', 'Le patient a un IMC élevé après AVC');
-- Résultat : 'accident_vasculaire_cerebral':7 'elev':5 'indice_masse_corporelle':4

Cas 3 : Recherche Insensible aux Accents

Par défaut, PostgreSQL respecte les accents dans le Full-Text Search.

-- Test standard
SELECT to_tsvector('french', 'café') @@ to_tsquery('french', 'cafe');
-- Résultat : false (café ≠ cafe)

-- Solution 1 : Extension unaccent
CREATE EXTENSION IF NOT EXISTS unaccent;

-- Créer un dictionnaire qui enlève les accents
CREATE TEXT SEARCH DICTIONARY french_unaccent (
    TEMPLATE = unaccent
);

-- Configuration sans accents
CREATE TEXT SEARCH CONFIGURATION french_no_accent (COPY = french);

ALTER TEXT SEARCH CONFIGURATION french_no_accent
    ALTER MAPPING FOR asciiword, word
    WITH french_unaccent, french_stem;

-- Test
SELECT to_tsvector('french_no_accent', 'café') @@
       to_tsquery('french_no_accent', 'cafe');
-- Résultat : true (accent enlevé)

9. Debugging et Analyse

Voir Comment un Texte est Traité

-- Debug complet du traitement
SELECT * FROM ts_debug('french', 'Les bases de données PostgreSQL');

Résultat :

alias     | description       | token      | dictionaries  | dictionary   | lexemes
----------+------------------+------------+---------------+-------------+---------
asciiword | Word, all ASCII  | Les        | {french_stem} | french_stem | {}  
blank     | Space symbols    |            | {}            |             |  
asciiword | Word, all ASCII  | bases      | {french_stem} | french_stem | {base}  
blank     | Space symbols    |            | {}            |             |  
asciiword | Word, all ASCII  | de         | {french_stem} | french_stem | {}  
blank     | Space symbols    |            | {}            |             |  
asciiword | Word, all ASCII  | données    | {french_stem} | french_stem | {donn}  
blank     | Space symbols    |            | {}            |             |  
asciiword | Word, all ASCII  | PostgreSQL | {french_stem} | french_stem | {postgresql}  

Colonnes importantes :

  • token : Mot original
  • dictionaries : Dictionnaires disponibles pour ce type
  • dictionary : Dictionnaire utilisé
  • lexemes : Résultat (vide = stop word)

Tester un Dictionnaire Spécifique

-- Tester directement un dictionnaire
SELECT ts_lexize('french_stem', 'données');
-- Résultat : {donn}

SELECT ts_lexize('french_stem', 'le');
-- Résultat : NULL (stop word)

-- Tester plusieurs mots
SELECT word, ts_lexize('french_stem', word) AS lexemes  
FROM unnest(ARRAY['courir', 'couraient', 'coureur', 'course']) AS word;  

Résultat :

word       | lexemes
-----------+---------
courir     | {cour}  
couraient  | {cour}  
coureur    | {cour}  
course     | {cours}  

Comparer Plusieurs Configurations

-- Voir les différences entre configurations
SELECT
    'simple' AS config,
    to_tsvector('simple', 'Les coureurs courent rapidement') AS result
UNION ALL  
SELECT  
    'french',
    to_tsvector('french', 'Les coureurs courent rapidement')
UNION ALL  
SELECT  
    'english',
    to_tsvector('english', 'Les coureurs courent rapidement');

10. Performance et Optimisation

Impact des Dictionnaires sur la Performance

Ordre de rapidité (du plus rapide au plus lent) :

  1. Simple : Aucun traitement (très rapide)
  2. Snowball : Algorithme de stemming (rapide)
  3. Synonym : Lookup dans un fichier (rapide pour petits fichiers)
  4. Ispell : Analyse morphologique (lent)
  5. Thesaurus : Phrases multi-mots (très lent)
-- Benchmark simplifié
EXPLAIN ANALYZE  
SELECT COUNT(*) FROM articles  
WHERE to_tsvector('simple', contenu) @@ to_tsquery('simple', 'postgresql');  
-- Temps : ~50ms

EXPLAIN ANALYZE  
SELECT COUNT(*) FROM articles  
WHERE to_tsvector('french', contenu) @@ to_tsquery('french', 'postgresql');  
-- Temps : ~65ms (15% plus lent à cause du stemming)

Optimisations

1. Pré-calculer les tsvector (Colonne Dédiée)

-- ✅ BON : Colonne précalculée
ALTER TABLE articles ADD COLUMN search_vector tsvector;  
UPDATE articles SET search_vector = to_tsvector('french', contenu);  
CREATE INDEX idx_search ON articles USING GIN(search_vector);  

SELECT * FROM articles WHERE search_vector @@ query;
-- Très rapide : lecture d'index

-- ❌ LENT : Calcul à chaque requête
SELECT * FROM articles  
WHERE to_tsvector('french', contenu) @@ query;  
-- Lent : recalcul du tsvector à chaque fois

2. Choisir la Configuration Appropriée

-- Pour du contenu sans stemming nécessaire (codes, IDs)
to_tsvector('simple', code_produit)

-- Pour du texte en langue naturelle
to_tsvector('french', description)

3. Limiter les Dictionnaires dans une Configuration

-- ❌ Trop de dictionnaires (lent)
ALTER TEXT SEARCH CONFIGURATION my_config
    ALTER MAPPING FOR asciiword
    WITH synonym1, synonym2, thesaurus1, ispell1, french_stem;
-- Chaque token passe par 5 dictionnaires !

-- ✅ Uniquement les nécessaires
ALTER TEXT SEARCH CONFIGURATION my_config
    ALTER MAPPING FOR asciiword
    WITH synonym1, french_stem;
-- 2 dictionnaires suffisent généralement

11. Bonnes Pratiques

✅ Choisir la Bonne Configuration pour Chaque Langue

-- ✅ BON : Configuration appropriée
to_tsvector('french', texte_francais)  
to_tsvector('english', english_text)  

-- ❌ MAUVAIS : Mauvaise configuration
to_tsvector('english', texte_francais)  -- Stemming anglais sur français !

✅ Documenter les Configurations Personnalisées

-- Documenter avec des commentaires
COMMENT ON TEXT SEARCH CONFIGURATION french_tech IS
'Configuration française avec synonymes techniques pour le secteur IT';

COMMENT ON TEXT SEARCH DICTIONARY tech_synonyms IS
'Synonymes techniques : postgres→postgresql, pgsql→postgresql, etc.';

✅ Tester les Configurations Avant Déploiement

-- Suite de tests
CREATE TABLE test_search_config (
    id SERIAL PRIMARY KEY,
    input TEXT,
    expected_lexemes TEXT[],
    config regconfig
);

-- Insérer des cas de test
INSERT INTO test_search_config (input, expected_lexemes, config) VALUES
    ('postgresql', ARRAY['postgresql'], 'french'),
    ('bases de données', ARRAY['base', 'donn'], 'french'),
    ('couraient', ARRAY['cour'], 'french');

-- Vérifier
SELECT
    input,
    expected_lexemes,
    to_tsvector(config, input) AS actual_tsvector,
    expected_lexemes = ARRAY(SELECT unnest(to_tsvector(config, input))) AS passed
FROM test_search_config;

✅ Gérer les Fichiers de Configuration Externes

# Structure recommandée
/usr/share/postgresql/16/tsearch_data/
├── french.stop            # Stop words français standard
├── custom_french.stop     # Stop words personnalisés
├── tech_synonyms.syn      # Synonymes techniques
├── medical_synonyms.syn   # Synonymes médicaux
└── ecommerce.syn          # Synonymes e-commerce

# Versionner ces fichiers
git add tsearch_data/  
git commit -m "Add custom dictionaries"  

✅ Prévoir un Fallback pour Langues Non Supportées

-- Trigger avec fallback
CREATE OR REPLACE FUNCTION safe_tsvector_trigger() RETURNS trigger AS $$  
DECLARE  
    config regconfig;
BEGIN
    BEGIN
        config := (NEW.langue || '_text_search')::regconfig;
    EXCEPTION WHEN OTHERS THEN
        config := 'simple'::regconfig;  -- Fallback sur 'simple'
    END;

    NEW.search_vector := to_tsvector(config, NEW.contenu);
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

12. Résumé et Aide-Mémoire

Configurations Linguistiques

-- Voir toutes les configurations disponibles
SELECT cfgname FROM pg_ts_config;

-- Utiliser une configuration
to_tsvector('french', texte)  
to_tsquery('french', 'recherche')  

-- Configuration par défaut
SHOW default_text_search_config;  
SET default_text_search_config = 'french';  

Types de Dictionnaires

Type Description Exemple
simple Pas de transformation Codes, IDs
snowball Stemming algorithmique Texte général
ispell Morphologie (dictionnaires) Haute précision
synonym Remplacement 1:1 Synonymes simples
thesaurus Phrases multi-mots Expressions
unaccent Suppression accents Insensible accents

Créer une Configuration Personnalisée

-- 1. Créer dictionnaires
CREATE TEXT SEARCH DICTIONARY mon_dict (
    TEMPLATE = snowball,
    Language = french,
    StopWords = custom
);

-- 2. Créer configuration
CREATE TEXT SEARCH CONFIGURATION ma_config (COPY = french);

-- 3. Modifier mappings
ALTER TEXT SEARCH CONFIGURATION ma_config
    ALTER MAPPING FOR asciiword, word
    WITH mon_dict, french_stem;

-- 4. Utiliser
to_tsvector('ma_config', texte)

Multilinguisme

-- Approche 1 : Colonne de langue
CREATE TABLE articles (
    langue VARCHAR(10),
    search_vector tsvector
);

-- Trigger qui choisit la config selon langue
-- (voir section 7 pour code complet)

-- Approche 2 : Colonnes séparées
CREATE TABLE produits (
    search_vector_fr tsvector,
    search_vector_en tsvector,
    search_vector_es tsvector
);

Debug

-- Analyse complète
SELECT * FROM ts_debug('french', texte);

-- Test d'un dictionnaire
SELECT ts_lexize('french_stem', 'mot');

-- Test d'une configuration
SELECT to_tsvector('french', texte);

Conclusion

Les dictionnaires et les configurations linguistiques sont les fondations du Full-Text Search multilingue et personnalisable dans PostgreSQL.

Points clés à retenir :

  1. Configurations pré-définies : 20+ langues supportées (french, english, spanish, etc.)

    • Stemming automatique
    • Stop words intégrés
    • Prêtes à l'emploi
  2. Types de dictionnaires :

    • Simple : Pas de transformation
    • Snowball : Stemming algorithmique (rapide)
    • Ispell : Morphologie (précis mais lent)
    • Synonym : Remplacements simples
    • Thesaurus : Phrases complexes
  3. Personnalisation :

    • Créer des dictionnaires spécifiques à votre domaine
    • Combiner plusieurs dictionnaires
    • Adapter les stop words
  4. Multilinguisme :

    • Colonne de langue + trigger dynamique
    • Colonnes séparées par langue
    • Configuration unifiée (cas simples)
  5. Performance :

    • Pré-calculer les tsvector (colonne dédiée)
    • Limiter le nombre de dictionnaires
    • Choisir le bon type selon les besoins

Les dictionnaires permettent d'adapter PostgreSQL Full-Text Search à n'importe quel contexte linguistique ou métier, tout en conservant d'excellentes performances. C'est ce qui en fait une solution véritablement professionnelle et polyvalente.

Prochaine étape : Explorez les index GIN avancés et les optimisations de performance pour des systèmes de recherche à grande échelle !


Ressources complémentaires :

⏭️ Index GIN pour la performance