web-dev-qa-db-fra.com

Comment puis-je manipuler la pertinence de la recherche dans le texte intégral MySQL pour rendre un champ plus "utile" qu'un autre?

Supposons que j'ai deux colonnes, mots-clés et contenu. J'ai un index de texte intégral sur les deux. Je souhaite qu'une ligne avec foo dans les mots-clés ait plus de pertinence qu'une ligne avec foo dans le contenu. Que dois-je faire pour que MySQL attribue un poids supérieur aux mots clés par rapport au contenu?

J'utilise la syntaxe "match against".

SOLUTION:

A été capable de faire ce travail de la manière suivante:

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 
36
Buzz

En fait, utiliser une instruction case pour créer une paire d'indicateurs pourrait être une meilleure solution:

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

Encore une fois, cela n’est valable que si toutes les correspondances de mots clés ont un rang supérieur à toutes les correspondances de contenu uniquement. J'ai également supposé qu'une correspondance dans le mot clé et le contenu correspond au rang le plus élevé.

20
notnot

Créer trois index de texte intégral

  • a) un sur la colonne mot clé
  • b) un sur la colonne de contenu
  • c) un sur le mot clé et la colonne de contenu

Ensuite, votre requête:

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) DESC

Le fait est que rel1 vous donne la pertinence de votre requête uniquement dans la colonne keyword (car vous avez créé l'index uniquement sur cette colonne). rel2 fait la même chose, mais pour la colonne content. Vous pouvez maintenant additionner ces deux scores de pertinence en appliquant la pondération de votre choix.

Cependant, vous n'utilisez aucun de ces deux index pour la recherche réelle. Pour cela, vous utilisez votre troisième index, qui se trouve sur les deux colonnes.

L'index sur (mot clé, contenu) contrôle votre rappel. Aka, ce qui est retourné.

Les deux index distincts (un sur les mots clés et un sur le contenu uniquement) contrôlent votre pertinence. Et vous pouvez appliquer vos propres critères de pondération ici.

Notez que vous pouvez utiliser un nombre quelconque d'index différents (ou modifier les index et les pondérations que vous utilisez au moment de la requête en fonction d'autres facteurs, par exemple ... recherchez uniquement le mot clé si la requête contient un mot stop ... réduisez le biais de pondération pour mots-clés si la requête contient plus de 3 mots ... etc).

Chaque index utilise beaucoup d'espace disque, donc plus d'index, plus de disque. Et à son tour, empreinte mémoire plus importante pour mysql. De plus, les insertions prendront plus de temps, car vous avez plus d'index à mettre à jour.

Vous devez analyser les performances (en prenant soin de désactiver le cache de requêtes mysql pour effectuer des analyses sinon vos résultats seront faussés) pour votre situation. Ce n'est pas Google Grade efficace, mais c'est assez facile et "out of the box" et c'est certainement certainement beaucoup mieux que votre utilisation de "comme" dans les requêtes.

Je trouve que ça marche vraiment bien.

75
mintywalker

Version simplifiée utilisant seulement 2 index en texte intégral (crédits tirés 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;

Ceci cherchera les deux colonnes indexées complètes par rapport à la variable keyword et sélectionnera la pertinence correspondante dans deux colonnes séparées. Nous exclurons les éléments sans correspondance (pertinence1 et pertinence2 valant zéro) et réorganiserons les résultats en fonction du poids accru de la colonne content_ft. Nous n'avons pas besoin d'index de texte intégral composite.

4
lubosdz

Je l'ai fait il y a quelques années, mais sans l'index de texte intégral. Je n'ai pas le code à portée de main (ancien employeur), mais je me souviens bien de la technique.

En un mot, j'ai sélectionné un "poids" dans chaque colonne. Par exemple:

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

S'il vous plait, oubliez n'importe quel code SQL de mauvaise qualité, cela fait quelques années que je n'avais plus besoin d'en écrire, et je le fais par tête-à-tête ...

J'espère que cela t'aides!

J.Js

0
Justin James

En mode booléen, MySQL prend en charge les opérateurs ">" et "<" pour modifier la contribution d'un mot à la valeur de pertinence attribuée à une ligne.

Je me demande si quelque chose comme ça fonctionnerait?

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
Tom

Autant que je sache, cela n’est pas supporté par la recherche en texte intégral MySQL, mais vous pouvez obtenir cet effet en répétant ce mot plusieurs fois dans le champ mot-clé. Au lieu d’avoir les mots-clés "foo bar", " foo bar foo bar foo bar ", ainsi foo et bar sont également importants dans la colonne mots-clés et, puisqu'ils apparaissent plusieurs fois, ils deviennent plus pertinents pour mysql.

Nous l'utilisons sur notre site et cela fonctionne.

0
adamJLev