2010-07-16 5 views
7

J'ai trois tables dans une base de données MySQL utilisés dans une application de bibliothèque musicale:MySQL Recherche par mot clé À travers plusieurs tables

Le tableau Genre a colonnes:

  • id
  • title (string)

La table Album comporte des colonnes:

  • id
  • genre_id (clé étrangère à Genre.id)
  • title (string)
  • artist (string)

et la table Track a colonnes:

  • id
  • album_id (clé étrangère à Album.id)
  • title (string)

Chaque Album peut avoir un certain nombre de Tracks, chaque Track a un Album, et chaque Album a un Genre.


Je veux mettre en œuvre une recherche par mot clé qui permet à l'utilisateur d'entrer un certain nombre de mots-clés et trouver tous Tracks que:

  • ont un title correspondant,
  • sont sur un Album avec correspondant à title ou artist,
  • ou sont sur un Album avec un Genre avec un correspondant title.

Les résultats doivent être triés par pertinence. Ce serait génial si chaque domaine avait un classement pour la pertinence. Par exemple, le title d'un Track pourrait être plus important que le title du Genre.

En outre, la solution doit utiliser une certaine forme de recherche partielle. Une recherche de rubber doit d'abord correspondre à tous les Tracks avec un title de Rubber, puis correspondre Tracks avec un title correspondant *rubber* (* = joker), puis passer à Albums, et ainsi de suite. Cependant, je ne suis pas tellement sur ces détails. Je suis à la recherche d'une solution plus générale que je peux ajuster pour répondre à mes besoins spécifiques.

Je devrais aussi mentionner que j'utilise une pile LAMP, Linux, Apache, MySQL et PHP.


Quelle est la meilleure façon de mettre en œuvre cette recherche par mot clé?


Mise à jour: J'ai essayé de mettre en œuvre ce via une recherche en texte intégral, et je suis venu avec les instructions SQL suivantes.

CREATE TABLE `Genre` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `title` text NOT NULL, 
    PRIMARY KEY (`id`), 
    FULLTEXT KEY (`title`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; 

INSERT INTO `Genre` VALUES(1, 'Rock'); 

CREATE TABLE `Album` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `genre_id` int(11) NOT NULL, 
    `title` text NOT NULL, 
    `artist` text, 
    PRIMARY KEY (`id`), 
    FULLTEXT KEY (`title`, `artist`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; 

INSERT INTO `Album` VALUES(1, 1, 'Rubber Soul', 'The Beatles'); 

CREATE TABLE `Track` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `album_id` int(11) NOT NULL, 
    `title` text NOT NULL, 
    PRIMARY KEY (`id`), 
    FULLTEXT KEY (`title`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; 

INSERT INTO `Track` VALUES(1, 1, 'Drive My Car'); 
INSERT INTO `Track` VALUES(2, 1, 'What Goes On'); 
INSERT INTO `Track` VALUES(3, 1, 'Run For Your Life'); 
INSERT INTO `Track` VALUES(4, 1, 'Girl'); 
+0

Vous recherchez une correspondance exacte ou une correspondance partielle? –

+0

Merci pour la réponse, j'ai ajouté plus d'informations à la question. –

Répondre

4

Je voudrais utiliser Apache Solr. Utilisez le Data Import Handler pour définir une requête SQL qui joint toutes vos tables, créez un index de texte intégral à partir du résultat des données jointes.


Les colonnes nommées comme args MATCH() doit être la colonne (s) que vous avez défini pour l'indice, dans le même ordre que vous avez défini dans l'index. Mais vous ne pouvez pas définir d'index (fulltext ou autre) sur plusieurs tables dans MySQL.

Vous ne pouvez pas faire ceci:

WHERE MATCH (g.title, a.title, a.artist, t.title) AGAINST ('beatles') 

Peu importe si vous utilisez le mode booléen ou en mode de langage naturel.

Vous devez faire ceci:

WHERE MATCH (g.title) AGAINST ('beatles') 
    OR MATCH (a.title, a.artist) AGAINST ('beatles') 
    OR MATCH (t.title) AGAINST ('beatles') 

Vous pouvez également être intéressé par ma présentation Practical Full-Text Search in MySQL.

1

Définir un index texte intégral sur les quatre colonnes que vous souhaitez rechercher, puis faites:

SELECT * FROM genre AS g 
    LEFT JOIN album AS a ON g.id = a.genre_id 
    LEFT JOIN tracks AS t ON a.id = t.album_id 
    WHERE MATCH (g.title, a.title, a.artist, t.title) AGAINST ('searchstring'); 

Le resullt seront triés par pertinence. Voir ici pour plus de détails sur la recherche du texte intégral: http://dev.mysql.com/doc/refman/5.0/en/fulltext-natural-language.html

+0

Le problème est, l'OP veut une correspondance partielle. Les caractères génériques fonctionnent mais uniquement pour les caractères suivant la chaîne de recherche. Vous ne pouvez pas appliquer le caractère générique * au début de la chaîne de recherche –

+0

Je pense que la seule façon de faire une correspondance exacte d'abord, puis une correspondance LIKE * abc * dans MySQL est d'utiliser deux requêtes. La solution Fulltext, je pense, est la plus proche que vous pouvez obtenir dans une seule requête. – JochenJung

+0

Cela fonctionne, mais il doit être en mode booléen. Ma requête modifiée est: 'SELECT * FROM Genre AS g LEFT JOIN Album comme ON g.id = a.genre_id LEFT JOIN Track AS t ON a.id = t.album_id OERE MATCH (g.title, a.title, a .artist, t.title) CONTRE ('beatles' EN MODE BOOLEAN) ' Lorsque j'exécute la requête sans 'IN BOOLEAN MODE', j'obtiens une erreur' # 1210 - Incorrect arguments to MATCH'. –

0

J'utiliser quelque chose comme Sphinx, u peut faire un index de votre requête, puis requête. C'est un peu difficile de se faire une idée mais les résultats sont 10 fois meilleurs que mysql AGAINST et vous n'aurez plus de problèmes avec la vitesse.

Questions connexes