MySQL match () contra () - ¿ordenar por relevancia y columna?

80

De acuerdo, estoy tratando de hacer una búsqueda de texto completo en varias columnas, algo simple como esto:

SELECT * FROM pages WHERE MATCH(head, body) AGAINST('some words' IN BOOLEAN MODE)

Ahora quiero ordenar por relevancia, (¿cuántas de las palabras se encuentran?) Que he podido hacer con algo como esto:

SELECT * , MATCH (head, body) AGAINST ('some words' IN BOOLEAN MODE) AS relevance 
FROM pages
WHERE MATCH (head, body) AGAINST ('some words' IN BOOLEAN MODE)
ORDER BY relevance

Ahora aquí viene la parte donde me pierdo, quiero priorizar la relevancia en la headcolumna.

Supongo que podría hacer dos columnas de relevancia, una para heady otra para body, pero en ese momento estaría haciendo la misma búsqueda en la tabla tres veces, y para lo que hago esta función, el rendimiento es importante, ya que La consulta se combinará y comparará con otras tablas.

Entonces, mi pregunta principal es , ¿existe una forma más rápida de buscar relevancia y priorizar ciertas columnas? (¿Y como beneficio adicional, posiblemente incluso hacer que la relevancia cuente el número de veces que las palabras aparecen en las columnas?)

Cualquier sugerencia o consejo sería genial.

Nota: Ejecutaré esto en un servidor LAMP. (WAMP en pruebas locales)

Kristoffer la Cour
fuente
¿Realmente tienes que poner MATCH ... AGAINST tanto en la cláusula SELECT como en la cláusula WHERE? ¿No puede usar un alias en la cláusula SELECT y hacer referencia al alias en la cláusula WHERE? Estoy tratando de usar declaraciones preparadas y esto me parece redundante / extraño.
S. Imp
2
No, como se indica en la documentación de MySQL desde 5.5, MATCH ... CONTRA se calculará una vez tanto en SELECT como en WHERE, por lo que no hay gastos adicionales.
Bob2u

Respuestas:

156

Esto puede dar una mayor relevancia a la parte principal que desee. No lo duplicará, pero posiblemente sea lo suficientemente bueno para usted:

SELECT pages.*,
       MATCH (head, body) AGAINST ('some words') AS relevance,
       MATCH (head) AGAINST ('some words') AS title_relevance
FROM pages
WHERE MATCH (head, body) AGAINST ('some words')
ORDER BY title_relevance DESC, relevance DESC

-- alternatively:
ORDER BY title_relevance + relevance DESC

Una alternativa que también desea investigar, si tiene la flexibilidad de cambiar el motor de base de datos, es Postgres . Permite establecer el peso de los operadores y jugar con el ranking.

Denis de Bernardy
fuente
14
Además, MySQL 5.6 admite búsquedas de texto completo en tablas InnoDB.
Jabari
1
¿Puede proporcionar un violín SQL para esto?
Usuario
¿Qué impacto negativo tienen las búsquedas múltiples? Necesitaría 4 coincidencias en mi SELECT ya que tengo 4 factores de peso diferentes. ¿Eso haría que el rendimiento fuera mucho menor?
Hasta el
@ToBe He visto en otras preguntas similares a más de una persona que dice que no hay una sobrecarga adicional con el uso de múltiples MATCHdeclaraciones, debido a la forma en que MySQL funciona internamente.
BadHorsie
Asegúrese de ejecutar estos dos. ALTER TABLE talk_webpages ADD FULLTEXT(head)yALTER TABLE talk_webpages ADD FULLTEXT(head, body)
Supun Kavinda
15

Solo agregando para quien pueda necesitar ... ¡No olvides modificar la tabla!

ALTER TABLE table_name ADD FULLTEXT(column_name);
Camilla
fuente
3
si ejecuta el comando anterior más de una vez, se crearán múltiples índices para la misma columna (s). Así que ejecute este comando solo una vez.
hakiko
Mejor aún, use CREATE FULLTEXT INDEX indexname en tablename (column_name (s)). También debería comprobar si el índice existe antes de intentar crearlo. Puede verificar si existe usando: SELECT INDEX_NAME FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_CATALOG= 'def' AND TABLE_SCHEMA= DATABASE () AND TABLE_NAME= 'tablename' AND INDEX_NAME= 'indexname';
Dave Hilditch
9

Nunca lo he hecho, pero parece que

MATCH (head, head, body) AGAINST ('some words' IN BOOLEAN MODE)

Debe dar un doble peso a los fósforos que se encuentran en la cabeza.


Solo lea este comentario en la página de documentos , creo que podría ser de valor para usted:

Publicado por Patrick O'Lone el 9 de diciembre de 2002 6:51 am

Cabe señalar en la documentación que IN BOOLEAN MODE casi siempre devolverá una relevancia de 1.0. Para obtener una relevancia significativa, deberá:

SELECT MATCH('Content') AGAINST ('keyword1 keyword2') as Relevance 
FROM table 
WHERE MATCH ('Content') AGAINST('+keyword1+keyword2' IN BOOLEAN MODE) 
HAVING Relevance > 0.2 
ORDER BY Relevance DESC 

Observe que está haciendo una consulta de relevancia regular para obtener factores de relevancia combinados con una cláusula WHERE que usa MODO BOOLEANO. El MODO BOOLEANO le brinda el subconjunto que cumple con los requisitos de la búsqueda BOOLEANA, la consulta de relevancia cumple el factor de relevancia y la cláusula HAVING (en este caso) asegura que el documento es relevante para la búsqueda (es decir, documentos que obtienen una puntuación inferior a 0.2 se consideran irrelevantes). Esto también le permite ordenar por relevancia.

Esto puede o no ser un error en la forma en que funciona IN BOOLEAN MODE, aunque los comentarios que he leído en la lista de correo sugieren que la clasificación de relevancia de IN BOOLEAN MODE no es muy complicada, por lo que se presta mal para proporcionar documentos relevantes. Por cierto, no noté una pérdida de rendimiento al hacer esto, ya que parece que MySQL solo realiza la búsqueda FULLTEXT una vez, aunque las dos cláusulas MATCH son diferentes. Utilice EXPLAIN para probar esto.

Por lo tanto, parecería que no necesita preocuparse por llamar dos veces a la búsqueda de texto completo, aunque aún debe "usar EXPLAIN para probar esto"

jisaacstone
fuente
1
Lamentablemente, agregar head dos veces a la función match () no funciona. ¿Quizás porque la consulta no cuenta el número de veces que aparecen las palabras? Y también he estado usando esa página a la que te refieres, pero por alguna razón no puedo hacer que funcione ... Todavía no he indexado mis columnas y, por lo tanto, no puedo buscar sin la etiqueta "IN BOOLEAN MODE". .
Kristoffer la Cour
Creo que una búsqueda no booleen devolvería # de ocurrencias, pero ¿booleen no?
jisaacstone
Lo investigaré más mañana, pero voy a esperar por ahora. Gracias por la respuesta, veremos si me ayuda cuando tenga esto.
Kristoffer la Cour
Tenía un problema al usar EN MODO BOOLEANO y luego ordenar por relevancia y esto resolvió mi problema con relevancia siempre devuelta como 1. Gracias.
Jazzy
Generar un campo de puntuación resolvió mi problema: estaba obteniendo resultados, pero muchos de ellos eran un completo ruido. Gracias, +1
Chris Baker
4

Yo también estaba jugando con esto. Una forma de agregar peso adicional es en el área ORDENAR POR del código.

Por ejemplo, si estuviera haciendo coincidir 3 columnas diferentes y quisiera ponderar más ciertas columnas:

SELECT search.*,
MATCH (name) AGAINST ('black' IN BOOLEAN MODE) AS name_match,
MATCH (keywords) AGAINST ('black' IN BOOLEAN MODE) AS keyword_match,
MATCH (description) AGAINST ('black' IN BOOLEAN MODE) AS description_match
FROM search
WHERE MATCH (name, keywords, description) AGAINST ('black' IN BOOLEAN MODE)
ORDER BY (name_match * 3  + keyword_match * 2  + description_match) DESC LIMIT 0,100;
Noah King
fuente
¿No es esta una consulta realmente pesada?
Beanow
5
Mueva las matemáticas a la declaración de selección y aligera mucho la carga. SELECT search.*, (MATCH (name) AGAINST ('black' IN BOOLEAN MODE) * 3) + (MATCH (keywords) AGAINST ('black' IN BOOLEAN MODE)*2 + MATCH (description) AGAINST ('black' IN BOOLEAN MODE)) AS totalScore , FROM search WHERE MATCH (name, keywords, description) AGAINST ('black' IN BOOLEAN MODE) ORDER BY totalScore DESC LIMIT 0,100;
InvertedSpear