La recherche fulltext de termes avec '-' (hyphen/dash) ne fonctionne pas bien sur MySQL

Request description

Les termes saisis dans des champs d’objets indexés (fonctionnalité de recherche globale/fulltext) qui contiennent le caractère ‘-’ (hyphen/dash/trait-d’union) sont actuellement reportés tel quels dans l’index fulltext. Ces termes ne peuvent pas être retrouvé par la fonction de recherche globale sauf si l’on spécifie une recherche exacte/complète en encadrant le terme recherche entre des double quotes. Il est de ce fait impossible de rechercher une partie de ces termes avec l’opérateur * (“A-B-C123” fonctionnera, A-B-C123 ne fonctionnera pas, “A-B-C*” ne fonctionnera pas, A-B-C* ne fonctionnera pas).

Le problème est abordé de manière détaillée ici : mysql - How to allow fulltext searching with hyphens in the search query - Stack Overflow

Afin de contourner ce problème, nous explorons une des solutions proposées qui consiste à “hacker” l’index fulltext en réinjectant dans l’index les termes avec des ‘-’ sans les ‘-’ → l’index fulltext de A-B-C123 contiendrait “… a-b-c123 abc123…” puis de retraiter les clauses de recherches dans l’index fulltext en supprimant les ‘-’ dans les termes recherchés (le hook preSearchInex supprime dans la liste des termes recherchés tout ‘-’ non précédé d’un espace → ne touche pas aux termes -xxx pour conserver le bénéfice de l’opérateur ‘-’ mais seulement aux termes x-x-x). Ainsi, un utilisateur peut rechercher le terme A-B-C* qui sera retraité avant la recherche en ABC* et ça devrait bien trouver ce que ça doit.

Cependant, pour pouvoir mettre en œuvre complètement la solution, il faudrait pouvoir retraiter les ‘-’ non précédés d’un espace lors de la constitution de l’index fulltext…
→ Existe-til un moyen de surcharger le comportement du socle pour obtenir ce résultat ? (un hook du type getUserKeyLabel mais appliqué lors du calcul de l’index fulltext nous permettrait d’activer ce comportement particulier de manière ciblée).
→ Est-il envisageable que ce comportement soit obtenu “en standard” ?

Bonjour,

Effectivement si on fait une recherche booléenne (“and” “or” “not” via “+” espace et “-” en MySQL)
on a un problème avec les opérateurs “+” et “-”.

La doc préconise de les entourer avec des double-quotes.

https://dev.mysql.com/doc/refman/8.0/en/fulltext-boolean.html

Il faudrait tester une requête du style : A"-"B"-"C* ou "A-B-C"*

Si c’est concluant, on pourra voir à les insérer nativement pour MySQL autour des hyphen entre 2 caractères.

(supprimer les - et les + n’est pas une bonne idée, car ça n’est plus la même donnée identifiable,
ex 123 serait égal à 1-23, 12-3, 1-2-3…)

Bonjour François,

merci beaucoup pour ton retour rapide.

Il faudrait tester une requête du style : A"-"B"-"C* ou "A-B-C"*

C’est fait mais malheureusement ça ne le fait pas…

Pour trouver une solution rapide et la moins crade possible, on a finalement opté pour l’ajout d’un attribut additionnel invisible (patchIndex) qui sera ajouté dans les objets concernés par le pb et un code partagé qui boucle sur une shortlist d’attributs d’objet configurés pour l’indexation et qui concaténera en preValidate les valeurs sans les ‘-’ (ça ajoutera un peu de volume BD et d’IO au runtime mais, bien que ça ne soit pas parfait (euphémisme), les résultats de recherches de type A-B-C* seront à peu près servis).

Ok ca reste un contournement.
Il faudrait voir comme escaper ces caractères spéciaux proprement (sans passer par Simplicité pour trouver la bonne syntaxe). En soit que le - et le + sans espace devant soit considéré comme un opérateur est une anomalie soit de MySQL soit de Simplicité qui formatte mal la requête.

Il est aussi possible d’explorer le mode de recherche. Simplicité utilise la recherche booléenne pur MySQL, peut-etre que la recherche “naturelle” vous conviendrait mieux.

MATCH (idx_ukey, idx_all) AGAINST ('A-B-C*' IN BOOLEAN MODE)
vs
MATCH (idx_ukey, idx_all) AGAINST ('A-B-C*' IN NATURAL LANGUAGE MODE)

Ca ne permet plus de faire des AND + et NOT -
mais j’imagine que ça continue de chercher par proximité non-case-sensitive.

Le MODE serait simple à rendre paramétrable pour MySQL.

Merci pour ton retour.

Le NATURAL LANGUAGE MODE a certainement un intérêt pour divers cas d’usage mais dans notre cas, le BOOLEAN MODE est préférable car il permet de plus facilement maîtriser ce qu’on veut trouver ou pas de manière ~relativement~ simple.

La cause racine du problème me semble également plutôt dans MySQL que dans Simplicité car il n’y a a priori aucun moyen d’escaper le ‘-’… du coup je l’ai extrait en amont à défaut de pouvoir faire mieux.

Ok merci pour tes retours.
On va faire des tests de notre côté pour essayer de trouver une solution.

1 Like

Bonjour François,

Merci beaucoup pour ton retour.

Le contournement appliqué dans notre contexte spécifique MySQL est pour l’instant déployé et fonctionnel tel que avec ses avantages et ses défauts.

J’ai commencé à tester les modalités de recherches fulltext offertes par PostGreSQL (qui est la brique SGBD déployée en standard dans nos architectures) et il y a là aussi des subtilités que je commence à fouiller (source : PostgreSQL: Documentation: 15: 12.3. Controlling Text Search).

En particulier, je vois dans la documentation et en testant que l’opérateur ‘-’ n’est pas supporté/interprété par la couche tsquery mais que la fonction websearch_to_tsquery le supporte (de manière simplifiée telle que je le comprends, en substituant ‘-’ par ‘!’).

En testant, j’ai l’impression que la recherche globale Simplicité traite correctement la négation de terme avec ‘!’ mais ignore l’opérateur ‘-’. Ce qui semble indiquer que de manière sous-jacente le runtime “pousse” les termes saisis par l’utilisateur en tant que tsquery et n’exploite pas le service proposé de traduction websearch_to_tsquery… Pour autant, si je saisi des termes en mode tsquery ça ne ramène plus rien…

  • +service +nissan +dacia -etude ramène ce qui est indexé avec service, nissan et dacia mais ramène aussi les études (le ‘-’ est ignoré)
  • +service +nissan +dacia !etude ramène ce qui est indexé avec service, nissan et dacia mais pas les études (ce qui est attendu)
  • 'service' & 'nissan' & 'dacia' & !'etude' (search en mode tsquery natif) ne ramène rien

J’ai donc l’impression que nous sommes entre les deux modes websearch (très simple et accessible) et tsquery (très puissant et formel).
→ Simplicité fait-il quelque-chose sur les termes avant de les pousser à PostGreSQL ?

Autre différence de comportement, avec PostGreSQL, il semble que l’opérateur ‘*’ ne soit pas nécessaire pour étendre les termes (gloss* pour couvrir gloss, glossary, glossaire, etc…). Le fait de mettre ‘*’ ou pas est traité de manière apparemment identique.

1 Like

Sur PGSQL, Simplicité fait des to_tsquery assez basiquement :

  • remplace juste les mots and par &, et or par |
  • et ajoute :* à chaque terme pour une recherche qui “commence par”

Les caractères ', !, + et - ne sont pas traités, du coup j’imagine que !'etude' devient !'etude':* et ça fait n’importe quoi.

Comme tu dis, il faudrait surement utiliser websearch_to_tsquery qui est plus user friendly = utilise le parser “malin” de PGSQL ajouté en v11 surement pour cette raison.

1 Like

Tout s’explique! :slight_smile:

Si j’ai bien compris, l’esprit de ce qui est actuellement implémenté consiste justement à présenter une “interface” simple/intuitive/robuste à l’utilisateur pour éviter de tomber dans les pièges d’un formalisme trop compliqué à maitriser…

Du coup, ce serait compliqué d’ajouter un pre-parsing websearch à la place du code actuel ?
Du point de vue de ma lorgnette (forcément limité), ça me semble mieux de rester cohérent avec la notice de PostGreSQL qui met en avant l’usage de l’opérateur ‘-’ (intuitif) via le formalisme websearch en amont de tsquery.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.