A única coisa que WITH não conseguia fazer

Prévia grátis 4 min de leitura

O curso de nível Júnior "ActiveRecord: fundamentos de CTE" fechou a primeira aula com uma promessa. Ele nomeou o que deixou de fora: uma CTE que referencia a si mesma, WITH RECURSIVE, para dados em árvore ou grafo, como um organograma ou as respostas de uma thread de comentários. Ele se chamou de metade não recursiva, de propósito. As sete aulas dele trataram de nomear uma subquery, fazer join com ela, encadear uma CTE em outra e desmontar um mito sobre materialização. Nenhuma precisou de uma query que referenciasse a própria saída. Este curso cumpre a promessa, na tabela que aquela aula citou.

Uma thread de comentários é a única tabela autorreferente desta aplicação

Community::Comment é o único model desta aplicação em que uma linha aponta para outra linha do mesmo tipo:

belongs_to :parent, class_name: "Community::Comment", optional: true
has_many :replies, class_name: "Community::Comment", foreign_key: :parent_id, dependent: :destroy

Uma thread numa aula é uma árvore: um comentário de topo, as respostas a ele e, em princípio, respostas às respostas, tão fundo quanto alguém continuar digitando. O código desta aplicação corta isso em um nível de respostas, de propósito:

def single_level_of_replies
  errors.add(:parent, :invalid) if parent&.parent_id.present?
end

Uma resposta pode definir o próprio parent_id, mas uma resposta a uma resposta é rejeitada, porque o pai dela já teria um parent_id. Então as threads de comentários aqui têm dois níveis, sempre. Quem garante é uma validação que os desenvolvedores escolheram escrever, e não o banco.

O Community::LessonThreadQuery, que carrega uma thread, tem a mesma forma. Uma query busca os comentários de topo, uma segunda busca todas as respostas a qualquer um deles, e o Ruby junta tudo com group_by(&:parent_id). Duas queries, dois níveis e nenhuma recursão, porque dois níveis é tudo o que os dados desta aplicação podem ter.

A pergunta que esta aplicação não precisa fazer

Deixe a single_level_of_replies de lado por um momento, e imagine que a aplicação aceitasse respostas de qualquer profundidade, como a maioria das plataformas de discussão aceita. A abordagem de duas queries do LessonThreadQuery para de funcionar no momento em que a profundidade deixa de ser fixa em dois. Respostas a respostas precisariam de uma terceira query, o nível seguinte de uma quarta, uma query por nível, sem como saber de antemão quantos níveis existem.

Uma CTE recursiva é a resposta do Postgres para esse problema. É uma query, escrita uma vez, que continua fazendo join de uma tabela com ela mesma até acabarem as linhas, seja qual for a profundidade da árvore. A validação desta aplicação significa que ela provavelmente nunca vai precisar de uma para comentários. O mecanismo é real, porém, e a forma do problema reaparece em schemas que esta aplicação não tem. São linhas autorreferentes, profundidade desconhecida e a vontade de uma query em vez de uma por nível. Pense em organogramas, árvores de categorias, uma hierarquia de permissões ou respostas encadeadas sem a regra de um nível desta aplicação.

Este curso constrói a CTE recursiva sobre community_comments, a tabela real desta aplicação, com as colunas e a foreign key reais. Ele só suspende o limite de profundidade dentro de dados de teste descartáveis, escritos para provar cada afirmação e apagados depois. Nunca toca num comentário que um aluno postou.

Pontos-chave

  • Community::Comment é o único model autorreferente desta aplicação: belongs_to :parent e has_many :replies apontam os dois para o próprio Community::Comment.
  • Uma validação, single_level_of_replies, limita as threads de comentários a dois níveis. O schema do banco não impõe limite nenhum; o limite é lógica da aplicação, não uma constraint.
  • O Community::LessonThreadQuery carrega uma thread de dois níveis com duas queries e um group_by em Ruby. Isso só funciona porque a profundidade é fixa e conhecida de antemão.
  • Uma CTE recursiva responde a outra pergunta: como ler uma tabela autorreferente numa query quando a profundidade não é fixa? Este curso constrói essa query sobre a tabela real, usando dados de teste descartáveis para passar do limite com segurança.
Aula 1 de 7 · grátis

Continue com a aula 2

with_recursive: o próprio método real do Rails, verificado

Monta uma query recursiva com o método with_recursive do Rails, explicando a âncora, o membro recursivo e as rodadas.

Com uma conta grátis você também tem os quizzes, seu progresso e suas anotações, e a discussão.

Digite para buscar...