2010-07-16 40 views
7

Tengo tres tablas en una base de datos MySQL utilizados en una aplicación de biblioteca de música:MySQL búsqueda de palabras clave en varias tablas

La tabla Genre tiene columnas:

  • id
  • title (cadena)

La tabla Album tiene columnas:

  • id
  • genre_id (clave externa a Genre.id)
  • title (cadena)
  • artist (cadena)

y la mesa Track tiene columnas:

  • id
  • album_id (clave externa a Album.id)
  • title (cadena)

Cada Album puede tener cualquier número de Tracks, cada uno tiene TrackAlbum, y cada uno tiene AlbumGenre.


Quiero poner en práctica una búsqueda por palabra clave que permite al usuario introducir cualquier número de palabras clave y encontrar toda Tracks que:

  • tener un title a juego,
  • están en un Album con una haciendo coincidir title o artist,
  • o están en un Album con un Genre con un title correspondiente.

Los resultados deben ser ordenados por relevancia. Sería genial si cada campo tuviera una clasificación por relevancia. Por ejemplo, el title de un Track podría ser más importante que el title del Genre.

Además, la solución debe usar alguna forma de búsqueda parcial. Una búsqueda de rubber primero debe coincidir con todos Tracks con un title de Rubber, a continuación, coincide con un Trackstitle juego *rubber* (* = comodín), a continuación, pasar a Albums, y así sucesivamente. Sin embargo, no estoy tan concentrado en estos detalles. Solo busco una solución más general que pueda modificar para que coincida con mis necesidades específicas.

También debería mencionar que estoy usando una pila LAMP, Linux, Apache, MySQL y PHP.


¿Cuál es la mejor manera de poner en práctica esta búsqueda por palabra clave?


Actualización: que he estado tratando de aplicar esto a través de una búsqueda de texto completo, y han llegado con las siguientes sentencias SQL.

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

¿Está buscando la coincidencia exacta o coincidencia parcial? –

+0

Gracias por la respuesta, he agregado más información a la pregunta. –

Respuesta

4

Yo usaría Apache Solr. Use Data Import Handler para definir una consulta SQL que una todas sus tablas, cree un índice de texto completo a partir del resultado de datos unidos.


Las columnas nombradas como argumentos para que coincida con() debe ser la columna (s) que ha definido para el índice, en el mismo orden que ha definido en el índice. Pero no puede definir ningún índice (texto completo u otro) en múltiples tablas en MySQL.

Así no se puede hacer esto:

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

No importa si usted está utilizando el modo booleano o el modo de lenguaje natural.

que tiene que hacer esto:

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

Usted puede también estar interesado en mi presentación Practical Full-Text Search in MySQL.

1

definir un índice de texto completo en las cuatro columnas que desea buscar y luego hacer:

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'); 

El resullt se ordenan por relevancia. ver aquí para más detalles sobre la búsqueda de texto completo: http://dev.mysql.com/doc/refman/5.0/en/fulltext-natural-language.html

+0

El problema es que el OP quiere una coincidencia parcial. Los comodines funcionan pero solo para los personajes que siguen la cadena de búsqueda. No se puede aplicar el comodín * al inicio de la cadena de búsqueda –

+0

. Creo que la única manera de hacer una coincidencia exacta primero y luego una coincidencia LIKE * abc * en MySQL es usar dos consultas. La solución de texto completo, creo, es lo más cercano que se puede obtener en una sola consulta. – JochenJung

+0

Esto funciona, aunque tiene que estar en modo booleano. Mi consulta modificada es: 'SELECT * FROM Género AS g LEFT JOIN Album AS a ON g.id = a.genre_id IZQUIERDA UNIR Pista AS t ON a.id = t.album_id WHERE MATCH (g.title, a.title, a .artist, T.Title) CONTRA ('beatles' en modo boolean) ' Cuando ejecuto la consulta sin' eN BOOLEANA MODE', aparece un '# 1210 - argumentos incorrectos a MATCH' error. –

0

me gustaría utilizar algo así como Sphinx, u puede hacer un índice de su consulta y entonces se le pregunta eso. Es un poco difícil entender, pero los resultados son 10 veces mejores que mysql EN CONTRA y no tendrás problemas más adelante con la velocidad.

Cuestiones relacionadas