Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(content): adjust validator function and create findChildrenCount function #366

Merged
merged 4 commits into from
Jun 7, 2022

Conversation

EmanoelCristhian
Copy link

@EmanoelCristhian EmanoelCristhian commented May 22, 2022

Created the function that counts how many children a content has recursively. tests have been
adjusted to test this new functionality.

#333

OBS: Ao adicionar a nova funcionalidade e rodar os testes, apenas os do children falharam. Ou seja, os testes de conteúdos raiz não estão testando caso tenha um dado a mais no JSON, apenas se tem a menos.

@EmanoelCristhian EmanoelCristhian added the back Envolve modificações no backend label May 22, 2022
@vercel
Copy link

vercel bot commented May 22, 2022

Someone is attempting to deploy this pull request to the TabNews Team on Vercel.

To accomplish this, the commit author's email address needs to be associated with a GitHub account.

Learn more about how to change the commit author information.

@EmanoelCristhian EmanoelCristhian self-assigned this May 22, 2022
@vercel
Copy link

vercel bot commented May 22, 2022

@EmanoelCristhian is attempting to deploy a commit to the TabNews Team on Vercel.

To accomplish this, @EmanoelCristhian needs to request access to the Team.

Afterwards, an owner of the Team is required to accept their membership request.

If you're already a member of the respective Vercel Team, make sure that your Personal Vercel Account is connected to your GitHub account.

@EmanoelCristhian EmanoelCristhian changed the title Retornar o número de conteúdos children pela API #333 feat(content): adjust validator function and create findChildrenCount function May 22, 2022
Copy link
Owner

@filipedeschamps filipedeschamps left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excelente implementação e o ponto sobre os testes reclamarem quando há um campo novo eu concordo 100%! Daqui para frente toda refatoração de testes deveria ter isso como boa prática 🤝

Em paralelo, fiz algumas sugestões para tentar manter o children_count igual entre os tipos de visualizações que um objeto content possa ter 👍

Comment on lines 53 to 60
const childrenCount = await content.findChildrenCount({
where: {
parent_id: contentFound.id,
},
});

contentFound['children_count'] = childrenCount;

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tava pensando aqui, talvez isso devesse ficar dentro do content.findAll(), uma vez que está sendo mesclado ao objeto um dado muito importante e que vai precisar existir em vários pontos da aplicação e não somente dentro desse controller. Por exemplo:

/api/v1/contents/[username]/[slug]  - está retornando o `children_count`
/api/v1/contents/ - não está retornando o `children_count`

Isso vai pesar entre a aplicação e o banco, pois para cada linha do findAll() vamos precisar bater de novo no Banco, mas para agora não vamos nos preocupar com otimização.

@@ -118,7 +118,7 @@ describe('GET /api/v1/contents/[username]/[slug]/children', () => {
expect(responseBody.length).toEqual(2);
expect(responseBody).toStrictEqual([
{
id: responseBody[0].id,
id: childBranchALevel1.id,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nossa, excelente alteração!

});

list.forEach((row) => {
if (table[row.parent_id]) {
table[row.parent_id].children_count++;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Muito delicinha as duas linhas adicionadas nessa implementação, porém o comportamento não vai ficar igual ao resultado obtido pelo findChildrenCount(), uma vez que ele só irá somar os filhos diretos (e não irá considerar o número de sub-filhos numa árvore profunda).

Tanto que isso é revelado pelos testes, que em resumo hoje estão fazendo o assertion considerando isso como resultado:

[
  {
    "id": "fdbcbe30-c6bb-4e1f-b5a2-402b7f096236",
    "children_count": 1
    "children": [
      {
        "id": "3fb91d2e-0049-4845-8dd6-4e13e58f3655",
        "children_count": 1
        "children": [
          {
            "id": "8662cc5e-da61-4468-aa4d-c86289920e2c",
            "children_count": 0
            "children": [              
            ],
          }
        ],
      }
    ],
  }
]

Quando deveria ser:

[
  {
    "id": "fdbcbe30-c6bb-4e1f-b5a2-402b7f096236",
    "children_count": 2   // Esse conteúdo tem 2 children
    "children": [
      {
        "id": "3fb91d2e-0049-4845-8dd6-4e13e58f3655",
        "children_count": 1
        "children": [
          {
            "id": "8662cc5e-da61-4468-aa4d-c86289920e2c",
            "children_count": 0
            "children": [              
            ],
          }
        ],
      }
    ],
  }
]

Isso fariam os resultados de children_count ficarem diferentes entre você consultar a árvore pelo nó do root ou diretamente o objeto de um content.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@filipedeschamps acredito que podemos melhorar essa semântica.

O que você acha de renomearmos o children_count para algo como children_deep_count ou children_recursive_count ou ainda chamá-lo de interactions ?

Do jeito que está causa esse tipo de confusão, principalmente para quem for consumir a API no futuro, tendo em vista que temos o children com length (que em algumas linguagens é chamado de count) igual a 1 e o children_count será igual a 2.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tembra, interessantíssimo. Essa diferença poderia até ser usada na interface para quando quisermos esconder os comentários após um certo nível e mostrar quantos comentários diretos existem.

O que estou me perguntando é se a propriedade deveria respeitar a modelagem no banco, dado que todo child, independente do nível, faz parte do grupo children. Então talvez deveria ser o contrário e os children diretos deveriam ser children_shallow_count, mas isso confunde a semântica como você apontou (a diferença entre filhos diretos e o valor encontrado no count que pode ser maior).

Dos nomes que você sugeriu, o que me fez entender sem pensar muito foi o children_deep_count 🤝

O que acha @EmanoelCristhian ? De deixarmos o children_count com o valor de filhos diretos, e o children_deep_count que soma toda a árvore embaixo de um nó. Lembrando que toda vez que um objeto content estiver presente, essas propriedades também precisarão estar presentes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

De deixarmos o children_count com o valor de filhos diretos

Realmente ele precisaria existir? Tendo em vista que (no JavaScript) um children.length obtém o mesmo resultado.

Por outro lado o children_deep_count já exige um certo processamento devido a interação recursiva, a qual estaria sendo executada direto no banco de dados.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eu me perguntei a mesma coisa, mas daí comecei a perguntar duas coisas:

  1. Qualquer tipo de client (em qualquer tecnologia) pode ser construído e cima da API, e talvez ter esse número computado seja uma mão na roda.
  2. Na lista de contents /api/v1/contents a propriedade children não é retornada (inclusive tava pensando até que o body nesse caso não deveria ser retornado, mas isso fica pra outra thread) e daí nesse caso o client pode decidir o que achar mais justo mostrar (shallow ou deep).

Mas posso estar over engineering 🤝

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2. Na lista de contents /api/v1/contents a propriedade children não é retornada

Parei de ler aqui 😅 Você me convenceu hehe.

Por mim fechado. Devemos ter o children_count e o children_deep_count.

inclusive tava pensando até que o body nesse caso não deveria ser retornado

Concordo total. Tráfego de dados desnecessário. Acredito que o objetivo deste endpoint seja apenas listar. Na outra thread poderíamos abrir para discussão o fato de torná-lo opcional, trazendo a informação apenas mediante o preenchimento de um parâmetro via query string, como with=body ou body=true.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@filipedeschamps @tembra, eu concordo totalmente com vocês.

Essa task me fez bugar um pouco quando eu pensei se era para eu retornar os filhos diretos ou todos os filhos de tal content.

Irei ajustar os códigos de acordo com o que foi acordado aqui!

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Show de bola @EmanoelCristhian ! A gente vai se bugando juntos até chegar numa conclusão 🤝 e fico muito feliz com sua contribuição, pois isso me possibilita nesse exato momento estar trabalhando no PR sobre o reset de senha 👍

export default Object.freeze({
findAll,
findOne,
findChildren,
findChildrenTree,
findChildrenCount,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dado as outras considerações, talvez a gente não deveria exportar essa função por enquanto.

},
],
children_count: 1,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Esse foi o detalhe que apontei no review no arquivo do model sobre a geração da árvore ter que considerar os children de forma deep.

Emanoel Cristhian Leão Marinho and others added 2 commits June 6, 2022 10:59
… function

Created the function that counts how many children a content has recursively. tests have been
adjusted to test this new functionality.
@vercel
Copy link

vercel bot commented Jun 6, 2022

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Updated
tabnews ✅ Ready (Inspect) Visit Preview Jun 7, 2022 at 2:53AM (UTC)

@filipedeschamps
Copy link
Owner

@EmanoelCristhian estou dando continuidade nessa branch, fiz rebase com a main, ajustei o que conversamos do seu commit e adicionei um novo commit que faz expor a propriedade children_deep_count. Fiz um push -f.

Conteúdo de teste (versão página):

https://tabnews-git-childrencount-tabnews.vercel.app/filipedeschamps/testando-childrendeepcount

Conteúdo de teste (versão API - /children):

https://tabnews-git-childrencount-tabnews.vercel.app/api/v1/contents/filipedeschamps/testando-childrendeepcount/children

@filipedeschamps
Copy link
Owner

Pessoal, decisão não sei se polêmica, mas removi o children_count e estou expondo apenas o children_deep_count. Fiz isso porque calcular esse valor no /children de um conteúdo não tem custo, pois está tudo em memória já e só envolve JavaScript dentro do método flatListToTree(). Agora, fora disso, quando você não está com a árvore de filhos em mãos, você precisa de uma query adicional para descobrir os filhos diretos e na minha visão zero vale a pena esse custo para obter a o número de filhos diretos.

Falando em custo, não sei se deveríamos nos preocupar agora, mas do jeito que foi implementado, para cada conteúdo nós fazemos uma query adicional em busca da quantidade de todos os filhos (independente da profundidade) e ela é feita de forma serial (dá para paralelizar, mas tenho medo de metralhar o banco de dados). Dado a isso, olha o gráfico abaixo do ambiente de Homologação que mostra a média de demora do endpoint /contents e eu nem preciso dizer a partir de que momento o deploy com essa feature entrou 😂

image

Por outro lado, não estamos usando índice e isso não ajuda nas buscas pelos valores.

Bom, as alterações estão no commit 3c06347 e aproveitei para adicionar testes que identificaram um bug na query de pegar o número de filhos 🤝 ela retornava o valor de 1 mesmo quando um content child não possuia children, pois retornava e contabilizava a si mesmo. Ela só retornava 0 se era um root content sem children.

Agora tempos children_deep_count tanto no objeto content isolado, quando na árvore dos filhos:

@rodrigoKulb
Copy link
Contributor

rodrigoKulb commented Jun 7, 2022

@filipedeschamps vendo um vídeo do Akita, que ele montou um banco de dados em JS em 3 dias hahaha!!

Lembrei dele falando sobre índice, que só serve para melhorar a velocidade da consulta e deixa o insert / update mais lento.

Pensando nessa lógica, você não acha uma boa cada PAI ser responsável pela contagem de seus filhos?

Exemplo o número de filhos só é alterado quando alguém:

  • Faz um comentário
  • Remove um comentário

Não é justo fazer sempre essa consulta em toda listagem de conteúdo, sabendo que não foi alterado.

Objetivo seria criar na tabela content a coluna children_count que seria atualizada nesses 2 momentos.

Quando alguém fizer um comentário, fazer um update no PAI somando mais UM.
Quando alguém remover o comentário fazer um update no PAI subtraindo UM.

@filipedeschamps
Copy link
Owner

Show! Outra forma que eu estava pensando era anotar em todos os contents o root_id (o parent máximo), porque daí é possível eliminar todas as queries recursivas (até no que monta a árvore de comentários). Para evitar quebrar essa referência a gente só precisaria travar que um content tenha o seu parent_id atualizado ou recalcular tudo se for atualizado.

De qualquer forma, eu gosto de quando a gente faz algo novo sem performance, e daí dá o tempo necessário para que alguém bolar algo muito mais otimizado, e como tem bastante cobertura de testes, fazer esse tipo de refatoração também é muito massa 🤝

@filipedeschamps
Copy link
Owner

Número de comentários aparecendo na lista:

https://tabnews-git-childrencount-tabnews.vercel.app/

image

Esta parte foi inspirada no PR #340 do @gabrielew

function AmountOfParents({ amount = 0 }) {
return amount !== 1 ? `${amount} comentários` : `${amount} comentário`;
}

@tembra
Copy link
Contributor

tembra commented Jun 7, 2022

@rodrigoKulb

Objetivo seria criar na tabela content a coluna children_count que seria atualizada nesses 2 momentos.

Perfeito! A solução do jeito que foi implementada não escala. Não havia falado antes pois sempre sugestões desse tipo são avaliadas para serem implementadas somente no futuro.

Quando os bancos de dados começam a crescer precisamos guardar esses dados computados dentro da própria tabela. Aqui minha experiência com ERP volta a confirmar a normalidade disso.

O único ponto de atenção necessário é no momento da implementação para que esses dados sempre fiquem íntegros. A ideia é sempre realizar essas atualizações em um único lugar para não sair do controle.

@filipedeschamps

Pessoal, decisão não sei se polêmica, mas removi o children_count

😆 Lembra da nossa discussão? Mas eu apoio ainda, apesar de você ter me convencido devido ao outro endpoint não ter a informação.

@filipedeschamps
Copy link
Owner

😆 Lembra da nossa discussão? Mas eu apoio ainda, apesar de você ter me convencido devido ao outro endpoint não ter a informação.

Show 😂 🤝

Estou fazendo o merge na main e vamos deixar o sistema nos avisar quando está na hora de programar outra estratégia 👍

@filipedeschamps
Copy link
Owner

Merged! Isso levou a Milestone 4 para 83% concluída 🤝

image

Let's goooooo!!!

@filipedeschamps
Copy link
Owner

@EmanoelCristhian você foi citado na última publicação comemorativa: https://www.tabnews.com.br/filipedeschamps/nova-melhoria-numero-de-comentarios-nas-publicacoes 😍

@vercel
Copy link

vercel bot commented Aug 10, 2022

An owner of the TabNews Team on Vercel declined @EmanoelCristhian's request to join.

In order for their commit to be deployed, @EmanoelCristhian must push and request access again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
back Envolve modificações no backend
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants