Como projetar uma tabela MySQL para uma nuvem de tags?
-
25-09-2019 - |
Pergunta
Eu tenho artigos no meu site e gostaria de adicionar tags que descreveriam cada artigo, mas estou tendo problemas com o design da tabela MySQL para tags. Eu tenho duas idéias:
- Cada artigo teria "tags" de campo, e as tags estariam no formato: "Tag1, Tag2, Tag3"
- Crie outra tabela chamada tags com campos: tag_name, artigo_id
Então, quando eu quero tags para o artigo com ID 1, eu corria
SELECT ... FROM tags WHERE `article_id`=1;
Mas, também gostaria de saber três artigos mais semelhantes comparando tags; portanto, se eu tiver um artigo que tenha tags "PHP, MySQL, Erlang" e 5 artigos com tags: "php, mysql", "erlang, ruby", "PHP Erlang", "MySQL, Erlang, JavaScript", eu escolheria 1., 3. e 4., já que esses 3 têm a maioria das mesmas tags com o artigo principal.
Além disso, outra pergunta: qual é a melhor maneira de obter 10 "tags mais usadas"?
Solução
Geralmente, para esse tipo de relacionamento muitos para muitos, existem três mesas:
- O "
article
" tabela- chave primária = id
- O "
tag
" tabela- chave primária = id
- Contém os dados de cada tag:
- nome, por exemplo
- UMA "
tags_articles
"Tabela, que atua como uma tabela de junção, e contém apenas:id_article
: Chave estrangeira que aponta para um artigoid_tag
: Chave estrangeira que aponta para uma tag
Dessa forma, não há duplicação dos dados de qualquer tag: para cada tag, existe um e apenas um, linha no tag
tabela.
E, para cada artigo, você pode ter várias tags (ou seja, várias linhas no tags_articles
tabela) ; E, é claro, para cada tags, você pode ter vários artigos.
Obter uma lista de tags para um artigo, com essa ideia, é uma questão de uma consulta adicional, como:
select tag.*
from tag
inner join tags_articles on tag.id = tags_articles.id_tag
where tags_articles.id_article = 123
Obter os três artigos "mais semelhantes" significariam:
- Selecione artigos com tags que o primeiro artigo tenha
- Use apenas aqueles que têm o número mais importante de tags idênticas
Não testado, mas uma ideia pode ser algo que seria assim:
select article.id, count(*) as nb_identical_tags
from article
inner join tags_articles on tags_articles.id_article = article.id
inner join tag on tag.id = tags_articles.id_tag
where tag.name in ('php', 'mysql', 'erlang')
and article.id <> 123
group by article.id
order by count(*) desc
limit 3
Basicamente, você:
- Selecione os IDs de artigos para cada tag que está presente em seu artigo inicial
- Como há uma junção interna, se um artigo no banco de dados tiver 2 tags que correspondam ao
where
Cláusula, sem ogroup by
Cláusula, haveria duas linhas para esse artigo - Obviamente, você não deseja selecionar novamente o artigo que já teve-o que significa que ele deve ser excluído.
- Como há uma junção interna, se um artigo no banco de dados tiver 2 tags que correspondam ao
- Mas, como você usa
group by article.id
, haverá apenas uma linha por artigo- Mas você poderá usar
count
, para descobrir quantas tags cada artigo tem em comum com o inicial
- Mas você poderá usar
- Então, é apenas uma questão de classificar por número de tags e obter apenas as três linhas das três linhas.
Outras dicas
Primeiro, você deseja usar a sugestão de Pascal Martin sobre o design da mesa.
Quanto a encontrar artigos semelhantes, aqui está algo para começar. Dado que @article_id é o artigo para o qual você deseja encontrar correspondências e @tag1, @tag2, @tag3 são as tags para esse artigo:
SELECT article_id, count(*)
FROM tags_articles
WHERE article_id <> @article_id
AND tag_id IN (@tag1, @tag2, @tag3)
GROUP BY article_id
ORDER BY count(*) DESC
LIMIT 3
Sim, mas você não respondeu minha pergunta principal, como obter três artigos mais semelhantes?
Resposta: Basta procurar os mesmos IDs de tags na tabela mesclada (tags_articles). Reúna -os e crie um padrão.
Por exemplo: Artigo 1 tem tags: 1,2 Artigo 2 tem tags: 2,3,4 O artigo 5 tem tags: 6,7,2 Artigo 7 tem tags: 7,1,2,3
Se você deseja os três artigos mais semelhantes para o artigo 1, deve procurar as tags 1,2. Você encontrará o artigo 7 mais semelhante e 2 e 5 têm algumas semelhanças.