• Aucun résultat trouvé

2.10 Suppression et modification de données

3.1.4 Recherches avec FULLTEXT

Nous allons maintenant voir comment utiliser la rechercheFULLTEXT, qui est un outil très puis-

sant, et qui peut se révéler très utile. Quelques rappels d’abord :

— un indexFULLTEXTne peut être défini que pour une table utilisant le moteurMyISAM;

— un indexFULLTEXTne peut être défini que sur une colonne de typeCHAR,VARCHARouTEXT

— les index “par la gauche” ne sont pas pris en compte par les indexFULLTEXT

Ça, c’est fait ! Nous allons maintenant passer à la recherche proprement dite, mais avant, je vais vous demander d’exécuter les instructions SQL suivantes, qui servent à créer la table que nous utiliserons pour illustrer ce chapitre. Nous sortons ici du contexte de l’élevage d’animaux. En effet, toutes les tables que nous créerons pour l’élevage utiliseront le moteur de stockage InnoDB. Pour illustrer la rechercheFULLTEXT, je vous propose de créer la tableLivre, contenant les co- lonnesid(clé primaire),titreetauteur. Les recherches se feront sur les colonnesauteurettitre, sé- parément ou ensemble. Il faut donc créer trois indexFULLTEXT: (auteur), (titre) et (auteur, titre).

3.1 Index

[[secret]] |sql | CREATE TABLE Livre ( | id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT, | auteur VARCHAR(50), | titre VARCHAR(200) | ) ENGINE = MyISAM; | |

INSERT INTO Livre (auteur, titre) | VALUES ('Daniel Pennac', 'Au bonheur des ogres'), | ('Daniel Pennac', 'La Fée Carabine'), | ('Daniel Pennac', 'Comme un roman'), | ('Daniel Pennac', 'La Petite marchande de prose'), | ('Jacqueline Harpman', 'Le Bonheur est dans le crime'), | ('Jacqueline Harpman', 'La Dormition des amants'), | ('Jacqueline Harpman', 'La Plage d''Ostende'), | ('Jacqueline Harpman', 'Histoire de Jenny'), | ('Terry

Pratchett', 'Les Petits Dieux'), | ('Terry Pratchett', 'Le Cinquième éléphant'), | ('Terry Pratchett', 'La Vérité'), | ('Terry Pratchett', 'Le Dernier héros'), | ('Terry Goodkind', 'Le Temple des vents'), | ('Jules Verne', 'De la Terre à la Lune'), | ('Jules Verne', 'Voyage au centre de la Terre'), | ('Henri-Pierre Roché', 'Jules et Jim'); | | CREATE FULLTEXT INDEX ind_full_titre | ON

Livre (titre); | | CREATE FULLTEXT INDEX ind_full_aut | ON Livre (auteur); | | CREATE FULLTEXT INDEX ind_full_titre_aut | ON Livre (titre, auteur); |

3.1.4.1 Comment fonctionne la recherche FULLTEXT ?

Lorsque vous faites une rechercheFULLTEXTsur une chaîne de caractères, cette chaîne est dé-

coupée en mots. **Est considéré comme un mot : toute suite de caractères composée de lettres, chiffres, tirets bas ||_|| et apostrophes ||’||**. Par conséquent, un mot composé, comme “porte- clés” par exemple, sera considéré comme deux mots : “porte” et “clés”. Chacun de ces mots sera ensuite comparé avec les valeurs des colonnes sur lesquelles se fait la recherche. Si la colonne contient un des mots recherchés, on considère alors qu’elle correspond à la recherche.

Lorsque MySQL compare la chaîne de caractères que vous lui avez donnée, et les valeurs dans votre table, il ne tient pas compte de tous les mots qu’il rencontre. Les règles sont les suivantes :

— les mots rencontrés dans au moins la moitié des lignes sont ignorés (règle des 50 %) ; — les mots trop courts (moins de quatre lettres) sont ignorés ;

— et les mots trop communs (en anglais,about,after,once,under,the…) ne sont également pas pris en compte.

Par conséquent, si vous voulez faire des recherches sur une table, il est nécessaire que cette table comporte au moins trois lignes, sinon chacun des mots sera présent dans au moins la moitié des lignes et aucun ne sera pris en compte.

Il est possible de redéfinir la longueur minimale des mots pris en compte, ainsi que la liste des mots trop communs. Je n’entrerai pas dans ces détails ici, vous trouverez ces informations dans la documentation officielle.

3.1.4.2 Les types de recherche

Il existe trois types de rechercheFULLTEXT: la recherche naturelle, la recherche avec booléen, et enfin la recherche avec extension de requête.

3.1.4.2.1 Recherche naturelle

Lorsque l’on fait une recherche naturelle, il suffit qu’un seul mot de la chaîne de caractères recherchée se retrouve dans une ligne pour que celle-ci apparaisse

dans les résultats. Attention cependant au fait que le mot exact doit se retrouver dans la valeur des colonnes de l’indexFULLTEXTexaminé.

Voici la syntaxe utilisée pour faire une rechercheFULLTEXT:

SELECT * -- Vous mettez évidemment les colonnes que vous voulez.

FROM nom_table

WHERE MATCH(colonne1[, colonne2, ...]) -- La (ou les) colonne(s) dans laquelle (ou lesquelles) on veut faire la recherche (index FULLTEXT correspondant nécessaire).

AGAINST ('chaîne recherchée'); -- La chaîne de caractères recherchée, entre guillemets bien sûr.

Si l’on veut préciser qu’on fait une recherche naturelle, on peut ajouterIN NATURAL LANGUAGE MODE. Ce n’est cependant pas obligatoire puisque la recherche naturelle est le mode de recherche par défaut.

SELECT *

FROM nom_table

WHERE MATCH(colonne1[, colonne2, ...])

AGAINST ('chaîne recherchée' IN NATURAL LANGUAGE MODE);

Premier exemple : on recherche “Terry” dans la colonneauteurde la tableLivre. SELECT *

FROM Livre

WHERE MATCH(auteur) AGAINST ('Terry');

id auteur titre

8 Terry Pratchett Les Petits Dieux

9 Terry Pratchett Le Cinquième éléphant 10 Terry Pratchett La Vérité

11 Terry Pratchett Le Dernier héros 12 Terry Goodkind Le Temple des vents

Deuxième exemple : On recherche d’abord “Petite”, puis “Petit” dans la colonnetitre. SELECT * FROM Livre WHERE MATCH(titre) AGAINST ('Petite'); SELECT * FROM Livre WHERE MATCH(titre) AGAINST ('Petit');

Résultat de la première requête :

id auteur titre

3.1 Index La deuxième requête (avec “Petit”) ne renvoie aucun résultat. En effet, bien que “Petit” se re- trouve deux fois dans la table (dans “La Petite marchande de prose” et “Les Petits Dieux”), il s’agit chaque fois d’une partie d’un mot, pas du mot exact.

Troisième exemple : on recherche “Henri” dans la colonneauteur. SELECT *

FROM Livre

WHERE MATCH(auteur) AGAINST ('Henri');

id auteur titre

16 Henri-Pierre Roché Jules et Jim

Ici par contre, on retrouve bien Henri-Pierre Roché en faisant une recherche sur “Henri”, puisque Henri et Pierre sont considérés comme deux mots.

Quatrième exemple : on recherche “Jules”, puis “Jules Verne” dans les colonnestitreetauteur. SELECT *

FROM Livre

WHERE MATCH(auteur, titre) AGAINST ('Jules');

SELECT *

FROM Livre

WHERE MATCH(titre, auteur) AGAINST ('Jules Verne');

id auteur titre

14 Jules Verne De la Terre à la Lune 16 Henri-Pierre Roché Jules et Jim

15 Jules Verne Voyage au centre de la Terre

id auteur titre

14 Jules Verne De la Terre à la Lune

15 Jules Verne Voyage au centre de la Terre 16 Henri-Pierre Roché Jules et Jim

Ces deux requêtes retournent les mêmes lignes. Vous pouvez donc voir que l’ordre des colonnes dansMATCHn’a aucune importance, du moment qu’un indexFULLTEXTexiste sur ces deux co-

lonnes. Par ailleurs, la recherche se fait bien sur les deux colonnes, et sur chaque mot séparément, puisque les premières et troisièmes lignes contiennent ‘Jules Verne’ dans l’auteur, tandis que la deuxième contient uniquement ‘Jules’ dans le titre.

Par contre, l’ordre des lignes renvoyées par ces deux requêtes n’est pas le même. Lorsque vous utilisezMATCH... AGAINSTdans une clauseWHERE, les résultats sont par défaut triés par perti-

nence.

La pertinence est une valeur supérieure ou égale à 0 qui qualifie le résultat d’une recherche

FULLTEXTsur une ligne. Si la ligne ne correspond pas du tout à la recherche, sa pertinence sera

de 0. Si par contre elle correspond à la recherche, sa pertinence sera supérieure à 0. Ensuite, plus la ligne correspond bien à la recherche (nombre de mots trouvés par exemple), plus la perti- nence sera élevée. Vous pouvez voir la pertinence attribuée à une ligne en mettant l’expression

MATCH... AGAINSTdans leSELECT.

Cinquième exemple : affichage de la pertinence de la recherche

SELECT *, MATCH(titre, auteur) AGAINST ('Jules Verne Lune')

FROM Livre;

id auteur titre

MATCH(titre, auteur) AGAINST (‘Jules Verne Lune’)

1 Daniel Pennac Au bonheur des ogres 0 2 Daniel Pennac La Fée Carabine 0 3 Daniel Pennac La Petite marchande de

prose

0 4 Jacqueline

Harpman

Le Bonheur est dans le crime 0 5 Jacqueline Harpman La Dormition des amants 0 6 Jacqueline Harpman La Plage d’Ostende 0 7 Jacqueline Harpman Histoire de Jenny 0 8 Terry Pratchett Les Petits Dieux 0 9 Terry Pratchett Le Cinquième éléphant 0

10 Terry Pratchett La Vérité 0

11 Terry Pratchett Le Dernier héros 0 12 Terry Goodkind Le Temple des vents 0 13 Daniel Pennac Comme un roman 0

14 Jules Verne De la Terre à la Lune 5.851144790649414 15 Jules Verne Voyage au centre de la

Terre 3.2267112731933594 16 Henri-Pierre Roché Jules et Jim 1.4018518924713135 En fait, écrire :

WHERE MATCH(colonne(s)) AGAINST (mot(s) recherché(s))

Cela revient à écrire :

WHERE MATCH(colonne(s)) AGAINST (mot(s) recherché(s)) > 0

Donc seules les lignes ayant une pertinence supérieure à 0 (donc correspondant à la recherche) seront sélectionnées.

3.1 Index

3.1.4.2.2 Recherche avec booléens

La recherche avec booléens possède les caractéris- tiques suivantes :

— elle ne tient pas compte de la règle des 50 % qui veut qu’un mot présent dans 50 % des lignes au moins soit ignoré ;

— elle peut se faire sur une ou des colonne(s) sur laquelle (lesquelles) aucun indexFULLTEXT

n’est défini (ce sera cependant beaucoup plus lent que si un index est présent) ; — les résultats ne seront pas triés par pertinence par défaut.

Pour faire une recherche avec booléens, il suffit d’ajouterIN BOOLEAN MODEaprès la chaîne re-

cherchée. SELECT *

FROM nom_table

WHERE MATCH(colonne)

AGAINST('chaîne recherchée' IN BOOLEAN MODE); -- IN BOOLEAN MODE à l'intérieur des parenthèses !

La recherche avec booléens permet d’être à la fois plus précis et plus approximatif dans ses re- cherches.

— Plus précis, car on peut exiger que certains mots se trouvent ou soient absents dans la ligne pour la sélectionner. On peut même exiger la présence de groupes de mots, plutôt que de rechercher chaque mot séparément.

— Plus approximatif, car on peut utiliser un astérisque ||*|| en fin de mot, pour préciser que le mot peut finir de n’importe quelle manière.

Pour exiger la présence ou l’absence de certains mots, on utilise les caractères ||+|| et ||-||. Un mot précédé par ||+|| devra être présent dans la ligne et inversement, précédé par ||-|| il ne

pourra pas être présent.

Exemple : Recherche sur letitre, qui doit contenir “bonheur” mais ne peut pas contenir “ogres”. SELECT *

FROM Livre

WHERE MATCH(titre)

AGAINST ('+bonheur -ogres' IN BOOLEAN MODE);

id auteur titre

4 Jacqueline Harpman Le Bonheur est dans le crime

Seule une ligne est ici sélectionnée, bien que “bonheur” soit présent dans deux. En effet, le second livre dont le titre contient “bonheur” est “Le Bonheur des ogres”, qui contient le mot interdit “ogres”.

Pour spécifier un groupe de mots exigés, on utilise les doubles guillemets. Tous les mots entre doubles guillemets devront non seulement être présents mais également apparaître dans l’ordre donné, et sans rien entre eux. Il faudra donc que l’on retrouve exactement ces mots pour avoir un résultat.

Exemple : recherche surtitre, qui doit contenir tout le groupe de mot entre guillemets doubles. SELECT *

WHERE MATCH(titre)

AGAINST ('"Terre à la Lune"' IN BOOLEAN MODE);

SELECT *

FROM Livre

WHERE MATCH(titre)

AGAINST ('"Lune à la Terre"' IN BOOLEAN MODE);

SELECT *

FROM Livre

WHERE MATCH(titre)

AGAINST ('"Terre la Lune"' IN BOOLEAN MODE);

Résultat première requête :

id auteur titre

14 Jules Verne De la Terre à la Lune

La première requête renverra bien un résultat, contrairement à la seconde (car les mots ne sont pas dans le bon ordre) et à la troisième (car il manque le “à” dans la recherche - ou il y a un “à” en trop dans la ligne, ça dépend du point de vue). “Voyage au centre de la Terre” n’est pas un résultat puisque seul le mot “Terre” est présent.

Pour utiliser l’astérisque, il suffit d’écrire le début du mot dont on est sûr, et de compléter avec un astérisque.

Exemple : recherche surtitre, sur tous les mots commençant par “petit”. SELECT *

FROM Livre

WHERE MATCH(titre)

AGAINST ('petit*' IN BOOLEAN MODE);

id auteur titre

3 Daniel Pennac La Petite marchande de prose 8 Terry Pratchett Les Petits Dieux

“Petite” et “Petits” sont trouvés.

Exemple : recherche surtitreetauteur, de tous les mots commençant par “d”. SELECT *

FROM Livre

WHERE MATCH(titre, auteur) AGAINST ('d*' IN BOOLEAN MODE);

id auteur titre

1 Daniel Pennac Au bonheur des ogres 2 Daniel Pennac La Fée Carabine

3.1 Index

id auteur titre

3 Daniel Pennac La Petite marchande de prose 13 Daniel Pennac Comme un roman

4 Jacqueline Harpman Le Bonheur est dans le crime 11 Terry Pratchett Le Dernier héros

8 Terry Pratchett Les Petits Dieux

5 Jacqueline Harpman La Dormition des amants

Chacun des résultats contient au moins un mot commençant par “d” dans son titre ou son auteur (“Daniel”, “Dormition”…). Mais qu’en est-il de “Voyage au centre de la Terre” par exemple ? Avec le “de”, il aurait dû être sélectionné. Mais c’est sans compter la règle qui dit que les mots de moins de quatre lettres sont ignorés. “De” n’ayant que deux lettres, ce résultat est ignoré. Pour en finir avec la recherche avec booléens, sachez qu’il est bien sûr possible de mixer ces dif- férentes possibilités. Les combinaisons sont nombreuses.

Exemple : recherche surtitre, qui doit contenir un mot commençant par “petit”, mais ne peut pas contenir le mot “prose”.

SELECT *

FROM Livre

WHERE MATCH(titre)

AGAINST ('+petit* -prose' IN BOOLEAN MODE); -- mix d'un astérisque avec les + et -

id auteur titre

8 Terry Pratchett Les Petits Dieux

3.1.4.2.3 Recherche avec extension de requête

Le dernier type de recherche est un peu particulier. En effet la recherche avec extension de requête se déroule en deux étapes.

1. Une simple recherche naturelle est effectuée.

2. Les résultats de cette recherche sont utilisés pour faire une seconde recherche naturelle. Un exemple étant souvent plus clair qu’un long discours, passons-y tout de suite.

Une recherche naturelle effectuée avec la chaîne “Daniel” sur les colonnesauteurettitredonnerait ceci :

SELECT *

FROM Livre

WHERE MATCH(titre, auteur) AGAINST ('Daniel');

id auteur titre

2 Daniel Pennac La Fée Carabine 1 Daniel Pennac Au bonheur des ogres 13 Daniel Pennac Comme un roman

Par contre, si l’on utilise l’extension de requête, en ajoutantWITH QUERY EXPANSION, on obtient ceci :

SELECT *

FROM Livre

WHERE MATCH(titre, auteur)

AGAINST ('Daniel' WITH QUERY EXPANSION);

id auteur titre

3 Daniel Pennac La Petite marchande de prose 13 Daniel Pennac Comme un roman

1 Daniel Pennac Au bonheur des ogres 2 Daniel Pennac La Fée Carabine

4 Jacqueline Harpman Le Bonheur est dans le crime

En effet, dans la seconde étape, une recherche naturelle a été faite avec les chaînes “Daniel Pen- nac”, “La Petite marchande de prose”, “Comme un roman”, “Au bonheur des ogres” et “La Fée Carabine”, puisque ce sont les résultats de la première étape (une recherche naturelle sur “Da- niel”). “Le Bonheur est dans le crime” a donc été ajouté aux résultats, à cause de la présence du mot “bonheur” dans son titre (“Bonheur” étant également présent dans “Au bonheur des ogres”)

Ainsi s’achève la présentation des requêtesFULLTEXT, ainsi que le chapitre sur les index.

3.1.4.3 En résumé

— Un index est une structure de données qui reprend la liste ordonnée des valeurs aux-

quelles il se rapporte.

— Un index peut se faire sur une ou plusieurs colonnes ; et dans les cas d’une colonne de type alphanumérique (CHAR,VARCHAR,TEXT, etc.), il peut ne prendre en compte qu’une partie

de la colonne (lesxpremiers caractères).

— Un index permet d’accélérer les recherches faites sur les colonnes constituant celui-ci. — Un index UNIQUEne peut contenir qu’une seule fois chaque valeur (ou combinaison de

valeurs si l’index est composite, c’est-à-dire sur plusieurs colonnes).

— Un indexFULLTEXT(réservé aux tables MyISAM) permet de faire des recherches complexes sur le contenu des colonnes le constituant.