Desenvolvendo o backend da nova versão do And After resolvi aprimorar o sistema de recomendações de posts relacionados do site e torná-lo mais relevante para os leitores.
Para acompanhar este post é necessário entender sobre relacionamento no banco de dados, saber um pouco como funciona o Data Mapper, a ferramenta ORM e também ter uma noção de relacionamento de tabelas com o Data Mapper.
Posts relacionados – Como funciona hoje?
Atualmente o sistema pega as tags de um post e, uma a uma, busca outros posts que utilizem a mesma tag. Estes posts são exibidos (sem repetição) e com um limite estipulado.
Isso significa que as últimas tags do post que o leitor está lendo podem ser ignoradas – pois possivelmente com as primeiras tags o sistema de relacionamento já tem posts suficientes para uma lista de recomendação.
Como deveria funcionar?
Para "afinar" o sistema de recomendação o ideal é levar em consideração todas as tags de dois posts que podem ser similares e ver quantas tags eles tem em comum. Quanto mais tags em comum, mais relacionado está o texto – porém não podemos esquecer de levar em consideração a data de publicação do mesmo, para evitar ficar exibindo apenas posts muito antigos.
Tecnologias
Estou utilizando PHP com o framework Code Igniter (tudo bonitinho com MVC) e banco de dados MySQL, a ferramenta ORM utilizada é o Data Mapper (leia também: como instalar e configurar o Data Mapper no Code Igniter).
Para atingir o objetivo criei a seguinte regra de negócio:
- Percorrer cada tag que o post atual tem e buscar os posts recentes que contém esta mesma tag.
- Atribuir um "ponto" para cada vez que um post é encontrado
- Ordenar a lista de posts relacionados pelos "pontos" (número de vezes que um post retornou como relacionado do atual)
Vamos ao código! A função abaixo recebe duas variáveis: $post (o id do post que o usuário está lendo) e $limit (quantos posts relacionados devem retornar no array de resultado).
function getRelatedPost($post, $limit) { //Pega o post atual e suas tags $p = new Post(); $p->select("id"); $p->where("id", $post); $p->get(1); $tags = $p->tag->get(); $arr = array(); //Percorre as tags do post atual foreach($tags as $t){ //pega os posts relacionados pela tag $related = new Post(); $related->where_related_tag("id", $t->id); $related->where("id <>", $post); $related->order_by("creationDate DESC"); $related->get($limit); //Se existirem posts, percorre cada post relacionado para esta tag if(!empty($related->id)){ foreach($related as $pr){ //Se o post já existir no array soma mais um ponto para ele //Se ele não existir cria ele no array com valor 1 $id = $pr->id; if(!empty($arr[$id])){ $arr[$id] = ($arr[$id]+1); }else{ $arr[$id] = 1; } } } } //Ordena o array pelos pontos //e pega cada post relacionado para retornar como resultado arsort($arr, SORT_REGULAR); $relArr = array(); foreach ($arr as $i => $value) { $relArr[] = $this->getPostById($i); } return($relArr); }
Qualquer dúvida, correção ou melhoria no código comente! 🙂