web-dev-qa-db-fra.com

Comment rechercher des mots avec trait d'union dans la recherche en texte intégral de PostgreSQL?

Je dois rechercher des mots coupés comme "bonjour", "bonsoir", etc.

Ma requête est:

select id, ts_headline(content,
                       to_tsquery('english','good-morning'),
                       'HighlightAll=true MaxFragments=100 FragmentDelimiter=$') 
from table 
where ts_content @@ to_tsquery('english','good-morning');

Lors de l'exécution de cette requête, j'obtiens également les résultats de 'bon' et 'matin' séparément. Mais je veux exactement des mots et des fragments assortis.
(Pour ts_content J'ai utilisé la même configuration par défaut english pour créer le tsvector.)

Comment puis-je rechercher de tels mots avec un trait d'union dans la recherche en texte intégral de PostgreSQL?

7
user3098231

Le mot clé ici est recherche de phrases, introduit avec Postgres 9.6 .

Utilisez l'opérateur tsquery FOLLOWED BY <-> ou l'un des opérateurs <N> associés . Ou mieux encore, utilisez la fonction phraseto_tsquery() pour générer votre tsquery.
Citant le manuel , il ...

produit tsquery qui recherche une phrase sans tenir compte de la ponctuation

Et:

phraseto_tsquery Se comporte comme plainto_tsquery, Sauf qu'il insère l'opérateur <-> (SUIVI DE) entre les mots survivants au lieu de l'opérateur & (AND). De plus, les mots vides ne sont pas simplement supprimés, mais sont pris en compte en insérant des opérateurs <N> Plutôt que des opérateurs <->. Cette fonction est utile lors de la recherche de séquences de lexèmes exactes, car les opérateurs FOLLOWED BY vérifient l'ordre des lexèmes et pas seulement la présence de tous les lexèmes.

Votre requête fonctionnerait comme ceci:

select id
     , ts_headline(content, phraseto_tsquery('english', 'good-morning')
                          , 'HighlightAll=true MaxFragments=100 FragmentDelimiter=$') 
from   tbl 
where  ts_content @@ phraseto_tsquery('english','good-morning');

phraseto_tsquery('english', 'good-morning') génère ceci tsquery:

'good-morn' <-> 'good' <-> 'morn'

Puisque "bonjour" est identifié comme asciihword (trait d'union ASCII Word), le mot complet dérivé est ajouté avant les composants. Le manuel:

Il est possible pour l'analyseur de produire des jetons qui se chevauchent à partir du même morceau de texte. À titre d'exemple, un mot avec trait d'union sera signalé à la fois comme le mot entier et comme chaque composant: (suivi d'un exemple)

to_tsvector() fait essentiellement la même chose à l'autre extrémité, donc tout correspond. Cela permet des options à grain fin avec des mots coupés. Ce qui précède ne trouve que "bonjour" avec un tiret (ou des variantes qui en découlent). Pour trouver toutes les chaînes avec "good" suivi de "morn" (ou des variantes qui en découlent) utilisez phraseto_tsquery('english','good morning') générant cette tsquery: 'good' <-> 'morn'

OTOH, vous pouvez appliquer correspondances exactes en ajoutant un autre filtre comme:

...
AND content ~* 'good-morning'  -- case insensitive regexp match

Ou:

...
AND content ILIKE '%good-morning%'

Semble un peu redondant pour l'œil humain, mais de cette façon, vous obtenez rapide prise en charge des index de texte intégral et exact correspond.

Ce dernier est principalement équivalent, mais différents (moins) caractères ont une signification spéciale dans le modèle LIKE et peuvent nécessiter un échappement. En relation:

Exemple pour montrer l'opérateur <N>:

phraseto_tsquery('english', 'Juliet and the Licks') génère ceci tsquery:

'juliet' <3> 'lick'

<3> Ce qui signifie que lick doit être le troisième lexème après juliet.

7
Erwin Brandstetter