2009-02-13 6 views
34

Supongamos que tengo dos columnas, palabras clave y contenido. Tengo un índice de texto completo en ambos. Quiero una fila con foo en las palabras clave para tener más relevancia que una fila con foo en el contenido. ¿Qué debo hacer para que MySQL pondere las coincidencias en palabras clave más altas que las del contenido?¿Cómo puedo manipular la relevancia de búsqueda de texto completo de MySQL para hacer que un campo sea más "valioso" que otro?

Estoy usando la sintaxis "match against".

SOLUCIÓN:

fue capaz de hacer este trabajo de la siguiente manera:

SELECT *, 
CASE when Keywords like '%watermelon%' then 1 else 0 END as keywordmatch, 
CASE when Content like '%watermelon%' then 1 else 0 END as contentmatch, 
MATCH (Title, Keywords, Content) AGAINST ('watermelon') AS relevance 
FROM about_data 
WHERE MATCH(Title, Keywords, Content) AGAINST ('watermelon' IN BOOLEAN MODE) 
HAVING relevance > 0 
ORDER by keywordmatch desc, contentmatch desc, relevance desc 

Respuesta

19

En realidad, el uso de una declaración de caso para hacer un par de banderas puede ser una solución mejor:

select 
... 
, case when keyword like '%' + @input + '%' then 1 else 0 end as keywordmatch 
, case when content like '%' + @input + '%' then 1 else 0 end as contentmatch 
-- or whatever check you use for the matching 
from 
    ... 
    and here the rest of your usual matching query 
    ... 
order by keywordmatch desc, contentmatch desc 

vez más, esto es sólo si todos los partidos de palabras clave rango superior al de todo el contenido de solo partidos. También asumí que una coincidencia en palabras clave y contenido es el rango más alto.

+0

OK, pude hacer que esto funcione. ¡Gracias! – Buzz

+3

Usar la instrucción like no es una excelente manera de ejecutar búsquedas. Primero, a menos que dividas cadenas, solo coincidirás en el orden exacto. es decir, buscar 'LIKE '% t-shirt red%'' no coincidirá con 'Red t-shirt' en su base de datos. En segundo lugar, terminas con un tiempo más alto para ejecutar tu consulta, ya que LIKE realiza un escaneo completo de la tabla. – ChrisG

+1

@ChrisG 'LIKE' realiza una exploración de tabla completa cuando se utiliza en la cláusula' FROM' que no está en 'SELECT' – gontard

0

Por lo que yo sé, esto no es compatible con MySQL búsqueda de texto completo, pero se puede lograr el efecto repitiendo de alguna manera esa palabra varias veces en el campo de palabra clave. En lugar de tener palabras clave "foo bar", tenga "foo bar foo bar foo bar", de esa manera tanto foo como la barra son igualmente importantes dentro de la columna de palabras clave, y dado que aparecen varias veces, se vuelven más relevantes para mysql.

Usamos esto en nuestro sitio y funciona.

-4

Si la métrica es solo que todas las coincidencias de palabras clave son más "valiosas" que todas las coincidencias de contenido, entonces puede usar una unión con recuentos de filas. Algo a lo largo de estas líneas.

select * 
from (
    select row_number() over(order by blahblah) as row, t.* 
    from thetable t 
    where keyword match 

    union 

    select row_number() over(order by blahblah) + @@rowcount + 1 as row, t.* 
    from thetable t 
    where content match 
) 
order by row 

Para algo más complicado que eso, en la que desea aplicar un peso real de cada fila, no sé cómo ayudar.

+0

He intentado esto, y terminó con errores de sintaxis. No creo que supiera qué poner en el orden por blahblah. Sugerencias? – Buzz

+0

Disculpe, no fue un ejemplo de copiar y pegar. El orden por en la cláusula anterior es el orden en que aplica los números de fila, por lo que debe ser el orden por el que normalmente ordenaría los resultados. – notnot

+0

Ahora que lo pienso, este duplicará los registros que coincidan con la palabra clave y el contenido. – notnot

-1

Bueno, eso depende de lo que quiere usted decir exactamente con:

quiero una fila con foo en las palabras clave tener más relevancia que una fila con foo en el contenido.

Si se refiere a que una fila con foo en las palabras clave debe venir antescualquier fila con foo en el contenido, entonces voy a hacer dos consultas separadas, una para las palabras clave y luego (posiblemente con pereza, solo si se solicita) el otro sobre el contenido.

0

Lo hice hace unos años, pero sin el índice de texto completo. No tengo el código a mano (antiguo empleador), pero recuerdo bien la técnica.

En pocas palabras, seleccioné un "peso" de cada columna. Por ejemplo:

select table.id, keyword_relevance + content_relevance as relevance from table 
    left join 
     (select id, 1 as keyword_relevance from table_name where keyword match) a 
    on table.id = a.id 
    left join 
     (select id, 0.75 as content_relevance from table_name where content match) b 
    on table.id = b.id 

favor forrgive cualquier SQL mala calidad aquí, que ha sido un par de años ya que necesitaba para escribir los hay, y estoy haciendo esto de la parte superior de mi cabeza ...

Esperanza esta ayuda!

J.Js

68

crear tres índices de texto completo

  • a) una en la columna de la palabra clave
  • b) una en la columna de contenido
  • c) un tanto de palabras clave y el contenido columna

Entonces, su consulta:

SELECT id, keyword, content, 
    MATCH (keyword) AGAINST ('watermelon') AS rel1, 
    MATCH (content) AGAINST ('watermelon') AS rel2 
FROM table 
WHERE MATCH (keyword,content) AGAINST ('watermelon') 
ORDER BY (rel1*1.5)+(rel2) 

El punto es que rel1 le da la relevancia de su consulta solo en la columna keyword (porque creó el índice solo en esa columna). rel2 hace lo mismo, pero para la columna content. Ahora puede agregar estos dos puntajes de relevancia aplicando la ponderación que desee.

Sin embargo, no está utilizando ninguno de estos dos índices para la búsqueda real. Para eso, usa su tercer índice, que está en ambas columnas.

El índice (palabra clave, contenido) controla su recuperación. Aka, lo que se devuelve.

Los dos índices independientes (uno solo en palabras clave, uno solo en contenido) controlan su relevancia. Y puede aplicar sus propios criterios de ponderación aquí.

Tenga en cuenta que puede usar cualquier cantidad de índices diferentes (o, varíe los índices y las ponderaciones que utiliza en el tiempo de consulta en función de otros factores quizás ... solo busque en palabra clave si la consulta contiene una palabra de parada ... el sesgo de ponderación de las palabras clave si la consulta contiene más de 3 palabras ... etc.).

Cada índice consume espacio en disco, por lo que hay más índices y más disco. Y a su vez, una mayor huella de memoria para mysql. Además, las inserciones tardarán más, ya que tiene más índices para actualizar.

Debe comparar el rendimiento (teniendo cuidado de desactivar la caché de consultas de MySQL para la evaluación comparativa de lo contrario, los resultados serán sesgados) para su situación. Esto no es eficiente en Google, pero es bastante fácil y "listo para usar" y es casi seguro mucho mejor que el uso de "me gusta" en las consultas.

Me parece que funciona muy bien.

+0

Funciona bien y tiene sentido. ¡Gracias! – Bretticus

+0

Parecía que no podía hacer que esto funcionara (tal vez porque no había agregado el tercer índice), pero cambiar la condición de dónde a: rel1> 0 OR rel2> 0 resolvió mi problema así que gracias. –

+1

@mintywalker, ¿el orden By no debería ser 'ORDER BY (rel1 * 1.5) + (rel2) DESC' para obtener el puntaje más alto y, por lo tanto, más relevante primero? – PanPipes

0

En modo booleano, MySQL admite el operador ">" y "<" para cambiar la contribución de una palabra al valor de relevancia asignado a una fila.

Me pregunto si algo como esto funcionaría?

SELECT *, 
MATCH (Keywords) AGAINST ('>watermelon' IN BOOLEAN MODE) AS relStrong, 
MATCH (Title,Keywords,Content) AGAINST ('<watermelon' IN BOOLEAN MODE) AS relWeak 
FROM about_data 
WHERE MATCH(Title, Keywords, Content) AGAINST ('watermelon' IN BOOLEAN MODE) 
ORDER by (relStrong+relWeak) desc 
0

necesitaba algo similar y utiliza la solución de la OP, pero me di cuenta de que el texto completo no coincide con palabras parciales. Por lo tanto, si 'sandía' está en palabras clave o contenido como parte de una palabra (como sandíasalesmanager) no coincide y no se incluye en los resultados debido al DONDE PARTIDO. Así que engañar un poco y pellizcada la consulta del OP a esto:

SELECT *, 
CASE WHEN Keywords LIKE '%watermelon%' THEN 1 ELSE 0 END AS keywordmatch, 
CASE WHEN Content LIKE '%watermelon%' THEN 1 ELSE 0 END AS contentmatch, 
MATCH (Title, Keywords, Content) AGAINST ('watermelon') AS relevance 
FROM about_data 
WHERE (Keywords LIKE '%watermelon%' OR 
    Title LIKE '%watermelon%' OR 
    MATCH(Title, Keywords, Content) AGAINST ('watermelon' IN BOOLEAN MODE)) 
HAVING (keywordmatch > 0 OR contentmatch > 0 OR relevance > 0) 
ORDER BY keywordmatch DESC, contentmatch DESC, relevance DESC 

Espero que esto ayude.

1

más simple versión utilizando sólo 2 índices de texto completo (créditos tomados de @mintywalker):

SELECT id, 
    MATCH (`content_ft`) AGAINST ('keyword*' IN BOOLEAN MODE) AS relevance1, 
    MATCH (`title_ft`) AGAINST ('keyword*' IN BOOLEAN MODE) AS relevance2 
FROM search_table 
HAVING (relevance1 + relevance2) > 0 
ORDER BY (relevance1 * 1.5) + (relevance2) DESC 
LIMIT 0, 1000; 
Cuestiones relacionadas