Curso
Então você quer criar seu próprio sistema de gerenciamento de conteúdo (CMS), também conhecido como blog? Esse é um exemplo clássico quando se aprende a usar um banco de dados, seja um sistema de gerenciamento de banco de dados relacional (RDBMS) ou um banco de dados nosql, porque ele explora uma quantidade potencialmente grande de dados, bem como as relações entre esses dados. O exemplo de um aplicativo de blog também funciona bem para outras necessidades de modelagem de dados.
Neste artigo, vamos ver o que fazer e o que não fazer na hora de criar seus documentos nosql no MongoDB. Mas, na verdade, não vamos desenvolver um aplicativo de blog, só vamos olhar as coisas do ponto de vista dos dados.
Os componentes de um aplicativo de blog
Antes de tentarmos criar um modelo de documento para um blog, é uma boa ideia dar um passo atrás e pensar em todos os componentes que podem ter dados associados a eles.
Um blog típico pode ter as seguintes características:
- Muitos usuários ou autores
- Várias postagens de blog para um único autor
- Muitos comentários para qualquer postagem do blog
Claro, essa lista de recursos pode ficar mais longa e complexa dependendo do que você precisa para o seu blog. Para simplificar, vamos modelar nossos dados com base no que foi dito acima.
O que você pode fazer, mas não deve fazer
A primeira coisa que vem à cabeça quando você vê os recursos acima é tratar cada item da lista como um documento separado. Essa é a abordagem típica que você usaria em um banco de dados relacional como o Postgres. No MongoDB, pode ser algo assim:
{
"_id": "author-1",
"name": "Nic Raboy",
"description": "I'm some dude named Nic."
}
O texto acima pode ser uma representação bem resumida de um autor. Claro, você poderia ter muitos outros campos, como email e outros, mas isso não é realmente importante aqui.
Então, poderíamos ter o seguinte para as postagens do blog:
{
"_id": "blog-1",
"author_id": "author-1",
"title": "MongoDB is Awesome!",
"content": "This is some blog content..."
}
Assim como o anterior, o documento do blog pode ter outros campos, como tags e o que mais você imaginar. O importante aqui é que existe uma referência de referência ( id ) entre os dois documentos. Mais sobre isso daqui a pouco, no entanto.
O modelo final do documento nesse cenário seria sobre comentários:
{
"_id": "comment-1",
"blog_id": "blog-1",
"username": "Anonymous User",
"content": "Hey great article, it really helped a lot!"
}
Mais uma vez, o documento de comentário nesse cenário tem uma relação de referência baseada em um valor de “ id ” entre o documento de comentário e o documento do blog. Isso é bem comum em um banco de dados relacional, e a verdade é que também funcionaria bem no MongoDB. Mas, isso não vai te dar a melhor experiência com o MongoDB.
Uma razão pela qual você não gostaria de fazer isso no MongoDB é por causa do desempenho.
Para juntar cada um desses três documentos, você deve usar uma operação de concatenação ( $lookup ) dentro de um pipeline de agregação. Essas operações $lookup não são baratas e podem afetar negativamente seu desempenho à medida que seu banco de dados cresce, ainda mais quando você tem mais de uma operação $lookup em seu pipeline.
Tem jeitos melhores de fazer isso no MongoDB.
Uma abordagem melhor e mais compatível com o MongoDB para modelagem de dados
Uma das primeiras regras do MongoDB é que os dados que são acessados juntos devem ser armazenados juntos. Armazenar cada recurso em um documento ou coleção separado quebra essa regra, porque não estamos mais fazendo uma única operação de busca para os dados apresentados ao usuário.
Vamos tentar resolver o problema de novo, modelando os documentos do nosso blog assim:
{
"_id": "author-1",
"name": "Nic Raboy",
"description": "I'm some dude named Nic.",
"posts": [
{
"title": "MongoDB is Awesome!",
"content": "This is some blog content...",
"comments": [
{
"username": "Anonymous User",
"content": "Hey great article, it really helped a lot!"
}
]
}
]
}
Tudo bem, então uma das principais regras do MongoDB está satisfeita no modelo acima. Temos um único documento que usa aninhamento para guardar todas as publicações do blog e todos os comentários com o autor.
Embora possa funcionar, provavelmente não é uma boa ideia modelar os documentos do seu aplicativo de blog dessa forma.
O MongoDB tem limites de tamanho de documentos e, quando você tem matrizes ilimitadas nesses documentos, corre o risco de ultrapassar esses limites. Sem falar que você pode ter problemas de desempenho à medida que as matrizes ficam maiores. Nesse caso, poderíamos ter um número ilimitado de publicações no blog e um número ilimitado de comentários por publicação. Em algum momento, a coisa vai ficar complicada.
O que pode funcionar melhor nesse exemplo é uma mistura das duas estratégias.
Lembre-se de que os autores, as publicações do blog e os comentários podem ter vários campos associados a eles. Neste exemplo, mantivemos tudo bem simples. Pensando nisso, sabemos que os dados mais acessados serão os do próprio blog. Dá uma olhada no novo design:
{
"_id": "blog-1",
"author": {
"_id": "author-1",
"name": "Nic Raboy",
},
"title": "MongoDB is Awesome!",
"content": "This is some blog content...",
"comments": [
{
"username": "Anonymous User",
"content": "Hey great article, it really helped a lot!"
}
]
}
O segundo documento que temos neste exemplo ainda seria o documento original com as informações do autor:
{
"_id": "author-1",
"name": "Nic Raboy",
"description": "I'm some dude named Nic."
}
A gente sabe que algumas informações do autor devem ser incluídas quando a gente mostra um artigo do blog, mas não precisamos necessariamente de todas as informações. A gente pode colocar as informações do autor que são necessárias junto com o _id dentro da postagem do blog, e se o usuário quiser saber mais sobre o autor, ele pode dar uma olhada mais a fundo no seu aplicativo. A operação ` $lookup ` não rola sempre, o que melhora o desempenho.
Agora, temos o problema da quantidade potencialmente infinita de comentários para qualquer artigo de blog atualmente anexado como uma matriz.
Tem algumas maneiras de lidar com isso e, no fim das contas, a decisão é sua:
- Você pode usar a abordagem mais simples e mantê-los como uma matriz, o que provavelmente não é uma boa ideia.
- Você pode voltar a colocar os comentários no formato original de um comentário por documento, todos com um identificador de referência para o artigo do blog e usando operações
$lookuppara incluí-los. - Você pode usar uma estratégia de arquivamento, guardando um número fixo de comentários em uma matriz dentro do artigo do blog e, ao mesmo tempo, criando um documento de comentários separado para cada um, permitindo que você só pesquise comentários extras se precisar.
- Você pode explorar o padrão de agrupamento, juntando comentários em “grupos” de cerca de 100 comentários por documento, consultando quantos grupos de comentários precisar de cada vez. A divisão em blocos evita o limite de tamanho do documento e também ajuda na paginação. Você também pode melhorar o desempenho dessa abordagem criando um índice no
blog_idpara buscar rapidamente comentários para qualquer artigo específico do blog. Mais informações sobre indexação podem ser encontradas na documentação do MongoDB. - Misture, combine e muito mais...
No caso do padrão de agrupamento, seus documentos de comentários podem ficar assim:
{
"_id": "comment-bucket-1",
"blog_id": "blog-1",
"comments": [
{
"username": "Anonymous User",
"content": "Hey great article, it really helped a lot!"
},
// More comments here...
],
"comment_count": 57
}
Nesse cenário, conforme novos comentários são criados, você procura um bucket que não esteja cheio. Você coloca o novo comentário na matriz e aumenta o valor da contagem. Embora essa estratégia dependa mais da lógica no nível do aplicativo para manter os buckets, ela vai trazer um desempenho melhor. Se você tem um mecanismo de rolagem infinita no seu blog, melhor ainda, porque assim você pode paginar melhor usando os buckets.
Embora esteja fora do escopo deste artigo específico, se você quiser ter uma ideia do que é necessário para adicionar um novo comentário, você filtraria a coleção de buckets de comentários (veja a consulta de exemplo abaixo) para buckets que correspondam ao blog_id e também tenham um comment_count menor que o valor escolhido.
Para a correspondência, você usaria o operador $push nos seus critérios de atualização e o operador $inc para aumentar o valor da contagem dos seus comentários. Tudo isso pode ser feito em uma única operação e, se você usar o sinalizador ` upsert `, poderá criar um novo bucket de comentários, caso ainda não exista um que corresponda aos seus critérios.
Exemplo de consulta de atualização:
db.commentBuckets.updateOne(
{ blog_id: "blog-1", comment_count: { $lt: 100 } },
{ $push: { comments: newComment }, $inc: { comment_count: 1 } },
{ upsert: true }
)
Conclusão
Por mais que fosse ótimo dizer que você deveria guardar tudo sobre o seu blog em um único documento aninhado, provavelmente não seria uma boa ideia. Assim como provavelmente não seria uma boa ideia implementar um modelo de dados que funciona bem com um banco de dados relacional, mas não necessariamente com um banco de dados de documentos como o MongoDB.
Com o MongoDB, você quer reduzir ao máximo suas relações referenciadas para obter o melhor desempenho possível.
Lembre-se do seguinte ao fazer o dimensionamento:
- Se você espera muitos comentários, pense em usar o padrão bucket ou alguma combinação dele junto com a incorporação parcial no documento do blog.
- Quais são suas necessidades de paginação?
- Indexe suas coleções com base em como você planeja fazer consultas em seu aplicativo.
Pra reforçar, pensa em como você vai mostrar os dados do blog na sua interface. Você realmente precisa de todas as informações do autor com a solicitação ou algumas delas já são suficientes? Você realmente quer carregar um milhão de comentários no artigo do seu blog ou basta carregar alguns poucos? Quando você realmente pensa sobre isso, ficaria surpreso com quantas operações de junção você não precisa.
Se você é novo no MongoDB, recomendo dar uma olhada no curso Introdução ao MongoDB em Python.
Perguntas frequentes
Posso passar o modelo de dados do meu aplicativo de blog de um banco de dados relacional para o MongoDB?
Você pode criar documentos simples com referências de identificação e isso vai funcionar, mas você não vai aproveitar o máximo do desempenho que poderia obter com o MongoDB.
Por que não guardar tudo em um único documento?
Tem limites de tamanho no MongoDB e, quando você tem matrizes ilimitadas, pode facilmente atingir esses limites.
O padrão do balde foi mencionado, mas tem outros?
Tem outros padrões que podem funcionar, mas o padrão bucket é uma boa opção quando se trata de grandes matrizes.
Quais são os limites de tamanho dos documentos do MongoDB?
Os documentos do MongoDB têm um tamanho máximo de 16 MB.
Como faço pra lidar com atualizações em dados aninhados (por exemplo, editar um comentário)?
Para dados incorporados, você pode usar operadores de atualização como $set e, para comentários agrupados, pode localizar o grupo e atualizar o comentário específico dentro dele.

Nic Raboy é Líder de Relações com Desenvolvedores na MongoDB, onde lidera uma equipe de desenvolvedores Python, Java, C# e PHP que criam conteúdo incrível para ajudar os desenvolvedores a serem bem-sucedidos na inclusão do MongoDB em seus projetos. Ele tem experiência com Golang e JavaScript e escreve com frequência sobre muitas de suas aventuras de desenvolvimento.



