MySQL: Ordenar los resultados de una búsqueda por relevancia selectiva con fulltext search
Entre las numerosas formas de lograr hacer un motor de búsqueda en MySQL, la mejor es el fulltext search. Pocos sitios lo usan y sigue siendo la solución más rápida y más completa de todas las opciones.
La única limitación del fulltext search es el límite de letras de las palabras a buscar ya que en la configuración del MySQL este límite viene por defecto en 4 letras, es decir, no va a tomar palabras menores a 4 letras para realizar la búsqueda. Pero esto puede ser cambiado y no es una excusa de peso para desplazar a las grandes ventajas del fulltext search: velocidad y rendimiento.
En este post intentaré explicar de forma simple como ordenar por relevancia una búsqueda en varios campos seleccionando los campos más importantes.
Muchos de ustedes habrán visto que muchos sitios ordenan sus resultados por relevancia o, en otras palabras, por cantidad de palabras encontradas. Esto es tan fácil de lograr que nos permite ir un poco más lejos para hacer, sin demasiado esfuerzo extra, un índice de relevancia utilizando varios campos de búsqueda como pueden ser un título y una descripción y varias palabras claves.
Vamos directamente al problema
La siguiente es una clásica búsqueda utilizando el afamado y popular LIKE en la tabla post del WordPress.
SELECT post_title, post_content
FROM posts
WHERE
post_title LIKE '%ipod%'
OR
post_content LIKE '%ipod%'
OR
post_title LIKE '%nano%'
OR
post_content LIKE '%nano%'
ORDER BY loquesea
Así en cambio se vería con el fulltextsearch. Utilizo este ejemplo debido a que muchos conocen esta tabla, pero a fines prácticos no puede ser usada para fulltext search porque no tiene los índices generados.
SELECT post_title, post_content
FROM lz_posts
WHERE
MATCH (post_title, post_content) AGAINST ('+"ipod"* +"nano"*' IN BOOLEAN MODE)
Claro que para que funcione, el campo deberá tener un índice fulltextsearch, lo que les comentaba más arriba.
Hasta acá, nada nuevo, pero hagámoslo más interesante y pasemos a generar un índice por el cual se le de más importancia a las palabras claves encontradas en el título y la descripción, luego en el título solamente y por último sólo en la descripción (por eso el nombre de relevancia selectiva, seleccionamos qué campo es más importante).
SELECT post_title, post_content, (
MATCH (post_title) AGAINST ('+"ipod"* +"nano"*' IN BOOLEAN MODE) +
MATCH (post_title, post_content) AGAINST ('+ipod*' IN BOOLEAN MODE) +
MATCH (post_title, post_content) AGAINST ('+nano*' IN BOOLEAN MODE)
) as relevance
FROM lz_posts
WHERE
MATCH (post_title, post_content) AGAINST ('+wordpress*')
ORDER BY relevance DESC
Eso generará un índice “relevance” con los 1 (booleanos) que cada MATCH encuentre de la siguiente forma:
1) Por cada palabra que encuentre en el título le suma 2
2) Por cada palabra que encuentre en el la descripción le suma 1
El “ORDER BY relevance DESC” hará que el registro con el mayor índice se genere primero.
Supongo que no hace falta aclarar que los MATCH después del primero se generan de forma automática dependiendo de la cantidad de keywords que el usuario haya buscado.
Esto no es nada nuevo, simplemente se trata de utilizar lo que ya existe de una forma útil y proactiva, pero me pareció interesante aclararlo ya que todavía hay personas que lo desconocen.
Cualquier input es bienvenido.









4 Comentarios
Miguel
Junio 26th, 2008 at 11:59 am
buen Post.
Un detalle, el límite de caracteres no es la única desventaja; tambien hay que tener en cuenta el espacio que ocupa el indice, el tener que actualizar el indice “manualmente” cada vez que se agregan registros, el tiempo para la búsqueda en miles de registros es bastante y pierde la ventaja respecto a otras búsquedas menos eficientes y la eficiencia esta lejos de otros algoritmos.
En sintesis, creo que es una muy buena opción para bases de datos pequeñas, con pocos cambios y en donde la búsqueda no es critica.
Saludos!
Lucas Zallio
Junio 26th, 2008 at 1:39 pm
Gracias por el comentario Miguel, sin embargo disiento en algunas cosas. Por ejemplo, es cierto que hay que generar el índice “cada tanto” pero se puede automatizar. El tema del tamaño del índice, a no ser que tengas un gran campo de texto no es tan crítico. Y si bien es cierto que hay muchos otros algoritmos que funcionan muy bien, considero que el fulltext search es el más efectivo en la relación uso-costo-beneficio.
Otra contra grande que me faltó poner es la incompatibilidad con otras bases y otros motores dentro del mismo MySql (como por ejemplo el ndb para el cluster)
Miguel
Junio 26th, 2008 at 3:13 pm
Si, se puede automatizar, pero cuando hablamos de grandes cantidades de información, puede llevar mucho tiempo y el índice va a ser bastante grande. Coincido en que para bases de datos no muy grandes ni complejas y en las que la exactitud de la búsqueda no es muy importante, es una de las mejores opciones.
Saludos!
Jordi Bassagañas
Julio 29th, 2008 at 10:25 am
Pues no sabía que Wordpress no usara índices full-text para las búsquedas…
Estoy haciendo un buscador que me trae de cabeza, a ver si alguien me puede ayudar. Resulta que mi buscador trabaja con talbas sobre las que se hacen muchas consultas INSERT, por lo que es necesario que sean de tipo InnoDB para que soporten transacciones y respeten la integridad referencial. Por otra parte, yo no quiero usar el LIKE para las búsquedas… ¿Qué se puede hacer?
Estoy pensando en duplicar el contenido (los campos que me interesan) en una tabla MyISAM, pero haciendo esto la base de datos va a crecer bastante, y no sé si esto se puede evitar.
Gracias por vuestra ayuda y un saludo