Eu queria saber qual é a melhor maneira de implementar um sistema de tags, como o usado no SO. Eu estava pensando nisso, mas não consigo encontrar uma boa solução escalonável.
Eu estava pensando em ter uma solução básica de 3 mesas: ter uma tags
mesa, uma articles
mesa e uma tag_to_articles
mesa.
Esta é a melhor solução para este problema ou existem alternativas? Usando esse método, a tabela ficaria extremamente grande com o tempo, e para pesquisar isso não é muito eficiente, eu suponho. Por outro lado, não é tão importante que a consulta seja executada rapidamente.
Respostas:
Acredito que você achará interessante esta postagem do blog: Tags: Esquemas de banco de dados
Solução “MySQLicious”
Nesta solução, o esquema possui apenas uma tabela, está desnormalizado. Este tipo é chamado de “solução MySQLicious” porque MySQLicious importa dados del.icio.us em uma tabela com esta estrutura.
Consulta de interseção (AND) para “search + webservice + semweb”:
Consulta de união (OR) para “search | webservice | semweb”:
Menos consulta para “search + webservice-semweb”
Solução “Scuttle”
Scuttle organiza seus dados em duas tabelas. Essa tabela “scCategories” é a tabela “tag” e tem uma chave estrangeira para a tabela “bookmark”.
Consulta de interseção (AND) para “bookmark + webservice + semweb”:
Primeiro, todas as combinações de bookmark-tag são pesquisadas, onde a tag é “bookmark”, “webservice” ou “semweb” (c.category IN ('bookmark', 'webservice', 'semweb')), então apenas os favoritos que todas as três marcas pesquisadas são levadas em consideração (HAVING COUNT (b.bId) = 3).
Consulta de união (OR) para “bookmark | webservice | semweb”: Basta omitir a cláusula HAVING e você terá união:
Menos (Exclusão) Consulta para “bookmark + webservice-semweb”, ou seja: bookmark AND webservice AND NOT semweb.
Deixar de fora HAVING COUNT leva à consulta de “bookmark | webservice-semweb”.
Solução “Toxi”
A Toxi criou uma estrutura de três mesas. Por meio da tabela “mapa de tags”, os favoritos e as tags são relacionados de n para m. Cada tag pode ser usada junto com diferentes marcadores e vice-versa. Este esquema de banco de dados também é usado pelo wordpress. As consultas são praticamente as mesmas da solução “scuttle”.
Consulta de interseção (AND) para “bookmark + webservice + semweb”
Consulta de União (OR) para “bookmark | webservice | semweb”
Menos (Exclusão) Consulta para “bookmark + webservice-semweb”, ou seja: bookmark AND webservice AND NOT semweb.
Deixar de fora HAVING COUNT leva à consulta de “bookmark | webservice-semweb”.
fonte
Nada de errado com sua solução de três mesas.
Outra opção é limitar o número de tags que podem ser aplicadas a um artigo (como 5 no SO) e adicioná-las diretamente à tabela do artigo.
A normalização do banco de dados tem suas vantagens e desvantagens, assim como conectar as coisas em uma tabela tem vantagens e desvantagens.
Nada diz que você não pode fazer as duas coisas. Repetir informações vai contra os paradigmas relacionais do banco de dados, mas se o objetivo for o desempenho, talvez seja necessário quebrar os paradigmas.
fonte
Sua proposta de implementação de três tabelas funcionará para marcação.
O estouro de pilha usa, no entanto, implementações diferentes. Eles armazenam tags na coluna varchar na tabela de posts em texto simples e usam indexação de texto completo para buscar posts que correspondem às tags. Por exemplo
posts.tags = "algorithm system tagging best-practices"
. Tenho certeza de que Jeff mencionou isso em algum lugar, mas esqueci onde.fonte
A solução proposta é a melhor - se não a única - maneira praticável que posso pensar de abordar a relação muitos-para-muitos entre tags e artigos. Portanto, meu voto é 'sim, ainda é o melhor.' Eu estaria interessado em quaisquer alternativas.
fonte
Se o seu banco de dados suportar matrizes indexáveis (como PostgreSQL, por exemplo), eu recomendaria uma solução totalmente desnormalizada - armazene as tags como uma matriz de strings na mesma tabela. Caso contrário, uma tabela secundária mapeando objetos para tags é a melhor solução. Se você precisar armazenar informações extras em relação às tags, poderá usar uma tabela de tags separada, mas não há motivo para introduzir uma segunda junção para cada pesquisa de tag.
fonte
Eu gostaria de sugerir MySQLicious otimizado para melhor desempenho. Antes disso, as desvantagens da solução Toxi (tabela 3) são
Se você tiver milhões de perguntas e houver 5 tags em cada uma, haverá 5 milhões de entradas na tabela de mapas de tags. Portanto, primeiro temos que filtrar 10 mil entradas de mapa de tag com base na pesquisa de tag e, novamente, filtrar as perguntas correspondentes dessas 10 mil. Portanto, ao filtrar se o id ártico é numérico simples, está tudo bem, mas se for do tipo UUID (32 varchar), a filtragem precisa de uma comparação maior, embora seja indexada.
Minha solução:
Sempre que uma nova tag for criada, tenha o contador ++ (base 10) e converta esse contador em base64. Agora, cada nome de tag terá um id de base64. e passe esse id para a IU junto com o nome. Desta forma, você terá no máximo dois char id até termos 4095 tags criadas em nosso sistema. Agora concatene essas várias tags em cada coluna de tag da tabela de perguntas. Adicione o delimitador também e torne-o classificado.
Então a mesa é assim
Durante a consulta, consulte o id em vez do nome real da tag. Uma vez que é SORTED , a
and
condição na tag será mais eficiente (LIKE '%|a|%|c|%|f|%
).Observe que um único delimitador de espaço não é suficiente e precisamos de um delimitador duplo para diferenciar as tags como
sql
emysql
porqueLIKE "%sql%"
retornarámysql
resultados também. Deveria estarLIKE "%|sql|%"
Eu sei que a pesquisa não está indexada, mas você ainda pode ter indexado em outras colunas relacionadas ao artigo, como autor / data / hora, caso contrário, levará a uma verificação completa da tabela.
Finalmente, com essa solução, nenhuma junção interna necessária, onde milhões de registros devem ser comparados com 5 milhões de registros na condição de junção.
fonte
Notas:
AUTO_INCREMENT
PK. Portanto, é melhor do que Scuttle.LIKE
com líder wild card; falsas visitas em substrings)Discussões relacionadas (para MySQL):
muitos: muitos listas ordenadas de otimização de tabelas de mapeamento
fonte