Usar Referências no MongoDB

MongoDBBeginner
Pratique Agora

Introdução

Neste laboratório, você aprenderá a usar referências do MongoDB para modelar relacionamentos entre dados. Você construirá um sistema simples de gerenciamento de biblioteca com coleções de authors e books. Através de passos práticos, você aprenderá a criar documentos, vinculá-los usando referências, consultar dados relacionados entre coleções, atualizar essas referências e melhorar o desempenho de consultas com índices. Este laboratório fornece uma base prática para modelagem de dados no MongoDB.

Criar Coleções e Referenciar Documentos

Nesta etapa, você configurará seu banco de dados e criará duas coleções: authors e books. Você aprenderá o conceito fundamental de referenciamento de documentos vinculando um livro ao seu autor.

Primeiro, abra o MongoDB Shell. Este shell interativo é onde você executará todos os seus comandos de banco de dados.

mongosh

Uma vez dentro do shell, você verá um prompt test>. Mude para um novo banco de dados chamado library_db. Se o banco de dados não existir, o MongoDB o criará quando você armazenar dados pela primeira vez.

use library_db

Agora, crie seu primeiro autor. Insira um documento na coleção authors. Estamos especificando um _id personalizado para este autor para facilitar a referência posterior.

db.authors.insertOne({
    _id: ObjectId("6633c9a5b4e3e8a5c8a8f8b1"),
    name: "Jane Austen",
    nationality: "British",
    birthYear: 1775
})

Em seguida, insira um documento na coleção books. O campo author_id contém o ObjectId do autor que você acabou de criar. É assim que você cria uma referência.

db.books.insertOne({
    title: "Pride and Prejudice",
    author_id: ObjectId("6633c9a5b4e3e8a5c8a8f8b1"),
    published: 1813,
    genre: "Classic Literature"
})

Você agora criou um relacionamento um-para-um. Para verificar isso, você pode recuperar os documentos que acabou de criar.

Primeiro, encontre o autor:

db.authors.findOne({ name: "Jane Austen" })

Exemplo de Saída:

{
  _id: ObjectId("6633c9a5b4e3e8a5c8a8f8b1"),
  name: 'Jane Austen',
  nationality: 'British',
  birthYear: 1775
}

Agora, encontre o livro e observe o campo author_id, que se vincula ao autor.

db.books.findOne({ title: "Pride and Prejudice" })

Exemplo de Saída:

{
  _id: ObjectId("..."),
  title: 'Pride and Prejudice',
  author_id: ObjectId("6633c9a5b4e3e8a5c8a8f8b1"),
  published: 1813,
  genre: 'Classic Literature'
}

Você pode permanecer no shell mongosh para as próximas etapas.

Vincular Múltiplos Documentos

Um autor geralmente escreve mais de um livro. Nesta etapa, você aprenderá a vincular múltiplos documentos "filhos" (livros) a um único documento "pai" (autor). Isso demonstra um relacionamento um-para-muitos.

Continue trabalhando no shell mongosh. Vamos adicionar mais dois livros de Jane Austen. Use o comando insertMany para inserir vários documentos de uma vez. Ambos os novos livros referenciarão o mesmo author_id.

db.books.insertMany([
    {
        title: "Sense and Sensibility",
        author_id: ObjectId("6633c9a5b4e3e8a5c8a8f8b1"),
        published: 1811,
        genre: "Classic Literature"
    },
    {
        title: "Emma",
        author_id: ObjectId("6633c9a5b4e3e8a5c8a8f8b1"),
        published: 1815,
        genre: "Classic Literature"
    }
])

Agora que Jane Austen tem três livros em nosso banco de dados, recupere todos eles usando o método find() e filtrando por author_id.

db.books.find({ author_id: ObjectId("6633c9a5b4e3e8a5c8a8f8b1") })

Exemplo de Saída:

[
  {
    _id: ObjectId("..."),
    title: 'Pride and Prejudice',
    author_id: ObjectId("6633c9a5b4e3e8a5c8a8f8b1"),
    published: 1813,
    genre: 'Classic Literature'
  },
  {
    _id: ObjectId("..."),
    title: 'Sense and Sensibility',
    author_id: ObjectId("6633c9a5b4e3e8a5c8a8f8b1"),
    published: 1811,
    genre: 'Classic Literature'
  },
  {
    _id: ObjectId("..."),
    title: 'Emma',
    author_id: ObjectId("6633c9a5b4e3e8a5c8a8f8b1"),
    published: 1815,
    genre: 'Classic Literature'
  }
]

Você também pode contar rapidamente quantos livros estão associados a um autor específico usando countDocuments.

db.books.countDocuments({ author_id: ObjectId("6633c9a5b4e3e8a5c8a8f8b1") })

Exemplo de Saída:

3

Esta consulta simples confirma eficientemente o número de documentos vinculados.

Consultar Entre Coleções com $lookup

Até agora, você recuperou livros usando um author_id conhecido. Uma abordagem mais poderosa é combinar dados de ambas as coleções em uma única consulta. Nesta etapa, você usará o estágio de agregação $lookup para realizar um join externo esquerdo (left outer join) da coleção books para a coleção authors.

Primeiro, adicione outro autor e um livro para tornar nossa consulta mais interessante.

db.authors.insertOne({
    _id: ObjectId("6633c9a5b4e3e8a5c8a8f8b2"),
    name: "Charles Dickens",
    nationality: "British",
    birthYear: 1812
})
db.books.insertOne({
    title: "Oliver Twist",
    author_id: ObjectId("6633c9a5b4e3e8a5c8a8f8b2"),
    published: 1837,
    genre: "Historical Fiction"
})

Agora, construa um pipeline de agregação. Esta consulta começará com a coleção books e "procurará" o autor correspondente para cada livro.

db.books.aggregate([
    {
        $lookup: {
            from: "authors",
            localField: "author_id",
            foreignField: "_id",
            as: "author_details"
        }
    }
])

O estágio $lookup possui os seguintes campos:

  • from: "authors": Especifica a coleção com a qual fazer o join.
  • localField: "author_id": O campo dos documentos de entrada (da coleção books).
  • foreignField: "_id": O campo dos documentos da coleção "from" (da coleção authors).
  • as: "author_details": O nome para o novo campo de array que é adicionado aos documentos de entrada.

Exemplo de Saída (para um documento):

{
  _id: ObjectId("..."),
  title: 'Pride and Prejudice',
  author_id: ObjectId("6633c9a5b4e3e8a5c8a8f8b1"),
  published: 1813,
  genre: 'Classic Literature',
  author_details: [
    {
      _id: ObjectId("6633c9a5b4e3e8a5c8a8f8b1"),
      name: 'Jane Austen',
      nationality: 'British',
      birthYear: 1775
    }
  ]
}

Como você pode ver, as informações do autor agora estão incorporadas em cada documento de livro sob o campo author_details. Isso permite que você consulte campos de ambas as coleções simultaneamente.

Atualizar e Manter Referências

Os dados nem sempre são estáticos. Você pode precisar corrigir erros ou remover dados, o que exige a atualização ou exclusão de documentos e suas referências. Nesta etapa, você aprenderá a atualizar uma referência e a lidar com documentos "órfãos".

Imagine que você descobriu que o livro "Emma" foi atribuído erroneamente a Jane Austen e deveria ser atribuído a Charles Dickens. Você pode corrigir isso usando o comando updateOne com o operador $set.

db.books.updateOne(
    { title: "Emma" },
    { $set: { author_id: ObjectId("6633c9a5b4e3e8a5c8a8f8b2") } }
)

Verifique a alteração encontrando o livro novamente e verificando seu author_id.

db.books.findOne({ title: "Emma" })

Exemplo de Saída:

{
  _id: ObjectId("..."),
  title: 'Emma',
  author_id: ObjectId("6633c9a5b4e3e8a5c8a8f8b2"),
  published: 1815,
  genre: 'Classic Literature'
}

Agora, vamos explorar o que acontece quando um documento pai é excluído. Se excluirmos um autor, quaisquer livros que referenciem esse autor se tornam "órfãos". Vamos excluir Charles Dickens de nosso banco de dados.

db.authors.deleteOne({ name: "Charles Dickens" })

O documento do autor desapareceu, mas os livros "Emma" e "Oliver Twist" ainda têm um author_id que aponta para o autor excluído. Isso pode causar problemas de integridade de dados. Em uma aplicação real, você implementaria lógica para lidar com isso, como excluir os livros órfãos ou reatribuí-los.

Para este laboratório, vamos limpar manualmente excluindo os dois livros órfãos.

db.books.deleteMany({ author_id: ObjectId("6633c9a5b4e3e8a5c8a8f8b2") })

Este comando remove todos os documentos da coleção books que referenciam o autor agora excluído, garantindo que nossos dados permaneçam consistentes.

Melhorar Desempenho de Consultas com Índices

Quando suas coleções crescem, consultas que filtram por um campo específico podem se tornar lentas. Isso ocorre porque o MongoDB precisa escanear cada documento para encontrar correspondências. Para otimizar isso, você pode criar um índice nos campos que consulta com frequência. No nosso caso, author_id na coleção books é um candidato perfeito.

Nesta etapa, você criará um índice no campo author_id para acelerar as buscas pelos livros de um autor.

Use o comando createIndex na coleção books. O argumento { author_id: 1 } informa ao MongoDB para criar um índice ascendente no campo author_id.

db.books.createIndex({ author_id: 1 })

O MongoDB processará isso em segundo plano. Assim que concluído, ele retornará uma mensagem confirmando que o índice foi criado.

Exemplo de Saída:

{
  "numIndexesBefore": 1,
  "numIndexesAfter": 2,
  "createdCollectionAutomatically": false,
  "ok": 1
}

Para verificar se o índice existe, você pode usar o comando getIndexes. Isso listará todos os índices na coleção books.

db.books.getIndexes()

Você deverá ver dois índices: o índice padrão em _id e o novo índice author_id_1 que você acabou de criar.

Exemplo de Saída:

[
  { "v": 2, "key": { "_id": 1 }, "name": "_id_" },
  { "v": 2, "key": { "author_id": 1 }, "name": "author_id_1" }
]

Com este índice implementado, qualquer consulta que filtre ou ordene por author_id, incluindo o estágio $lookup que você usou anteriormente, será significativamente mais rápida em grandes conjuntos de dados.

Finalmente, você pode sair do shell do MongoDB.

exit

Resumo

Neste laboratório, você aprendeu os fundamentos do uso de referências de documentos no MongoDB. Você começou criando coleções e vinculando documentos com referências ObjectId. Em seguida, praticou o gerenciamento de relacionamentos um-para-muitos, consultando entre coleções usando o poderoso estágio de agregação $lookup e mantendo a integridade dos dados atualizando e limpando referências. Finalmente, você melhorou o desempenho das consultas criando um índice em um campo de referência. Essas habilidades são essenciais para construir aplicações escaláveis e eficientes com o MongoDB.