2010-09-24 13 views
7

J'essaie de savoir comment commander des articles avec des étiquettes correspondantes par le nombre d'étiquettes qui correspondent.Commander des articles avec les mots-clés correspondants par le nombre de mots-clés qui correspondent

Disons que vous avez trois tables MySQL:

  • tags(tag_id, title)
  • articles(article_id, some_text)
  • articles_tags(tag_id, article_id)

Maintenant que vous avez quatre articles où:

article_id = 1 a des balises " humour, "" f unny, "et" hilarant ".

article_id = 2 a des balises "funny", "stupide", et "goofy".

article_id = 3 a des balises "funny", "stupide" et "goofy".

article_id = 4 a l'étiquette "complètement grave".

Vous devez trouver tous les articles liés à article_id = 2 par au moins un tag correspondant, et retourner les résultats dans l'ordre des meilleurs résultats. En d'autres termes, article_id = 3 devrait venir en premier, avec article_id = 1 deuxième, et article_id = 4 ne devrait pas apparaître du tout.

Est-ce quelque chose qui est faisable dans les requêtes SQL ou seul, ou est-ce mieux adapté à quelque chose comme Sphinx? Si le premier, quel type de requête devrait être fait, et quel genre d'index devrait être créé pour les résultats les plus performants? Si ce dernier, s'il vous plaît ne développez.

Répondre

10

Essayez quelque chose comme ceci:

select article_id, count(tag_id) as common_tag_count 
from articles_tags 
group by tag_id 
where tag_id in (
    select tag_id from articles_tags where article_id = 2 
) and article_id != 2 
order by common_tag_count desc; 

Syntaxe peut avoir besoin d'un peu de peaufinage pour MySQL.

ou celui qui fonctionne réellement: ;-)

SELECT at1.article_id, Count(at1.tag_id) AS common_tag_count 
FROM articles_tags AS at1 INNER JOIN articles_tags AS at2 ON at1.tag_id = at2.tag_id 
WHERE at2.article_id = 2 
GROUP BY at1.article_id 
HAVING at1.article_id != 2 
ORDER BY Count(at1.tag_id) DESC; 
+0

La deuxième syntaxe est fantastique et a fonctionné exactement comme je le souhaitais. Merci beaucoup! –

2

Quelque chose ressemblante:

SELECT a.* 
FROM articles AS a 
INNER JOIN articles_tags AS at ON a.id=at.article_id 
INNER JOIN tags AS t ON at.tag_id = t.id 
WHERE t.title = 'funny' OR t.title = 'goofy' OR t.title = 'silly' AND a.id != <article_id> 
GROUP BY a.id 
ORDER BY COUNT(a.id) DESC 

Avec seulement les indices habituels, en supposant articles_tags a PK de (article_id, tag_id), et un index sur tags.title