Aula 10 - MongoDB e Mongoose
Exemplos de aula
Tecnologias

O MongoDB é software multi-plataforma de base de dados orientado a documentos e ao modelo JSON. Sua primeira versão foi públicada em 2009.
Suas características permitem a criação de dados aninhados em hierarquias complexas e flexíveis, mas que ainda permitem a indexação e busca eficiente.
O MongoDB pode ser instalado em sua versão Comunity ou utilizado diretamente através da nuvem da Atlas.
Site do MongoDB Community Edition
Site da Atlas

A biblioteca Mongoose fornece uma interface para validação, casting e lógica de negócio com o MongoDB.
Apesar de não ser estritamente necessário, o mongoose facilita a maioria das operações habituais para implementação de um CRUD.
Para instalar o mongoose, utilize o comando:
npm install mongoose --saveEm seguida, vamos implementar a interação com o banco de dados seguindo os seguintes passos:
- Criar uma conexão
- Criar um esquema
- Realizar operações
Quick Start na Documentação
Usando o Mongoose
O primeiro para estabelecer uma conexão entre a aplicação e o servidor mongodb, é utilizar a seguinte linha de código:
const mongoose = require('mongoose')
mongoose.connect('mongodb://username:password@host:port/database?options...', {useNewUrlParser: true})
Onde:
mongodb- protocolousername:password- (opcional) define usuário e senhahost- define endereço do servidorport- define a porta do servidor (padrão: 27017)database- nome da base de dados a ser utilizada (será criada caso não exista)
A opção {useNewUrlParser: true} é utilizada atualmente para efeitos de compatibilidade com versões anteriores da biblioteca.
O Mongoose permite a implementação de eventos utilizando a função ``on()``:
const mongoose = require('mongoose')
mongoose.connection.on('connected', function(){
console.log("MongoDB conectado")
Os tipos de eventos incluem:
connecting- Emitido quando o mongoose tenta iniciar uma conexãoconnected- Emitido quando o mongoose realiza a conexão com sucessodisconnected- Emitido quando o mongoose perde a conexão com o servidorerror- Emitido em caso de erro com a conexão
Mais eventos na Documentação
Com a conexão estabelecida, podemos definir esquemas que serão utilizados como modelos para os dados que iremos trabalhar na aplicação:
var mongoose = require('mongoose')
var blogSchema = new mongoose.Schema({
title: String,
author: String,
body: String,
comments: [{ body: String, date: Date, default: [] }],
date: { type: Date, default: Date.now },
})
var Blog = mongoose.model('Blog', blogSchema)
Repare que um esquema geralmente segue as mesmas características do exemplo acima:
- Deve ser criado instanciando um objeto do tipo
Schema - Deve ser atribuído um nome de classe, como no caso
Blog - Deve ser especificado o tipo de dado de cada atributo, atribuindo diretamente o tipo de dado (Ex.:
title: String, ou usando o formato de objeto (Ex.:title: { type: String }).
Outro comportamento padrão da biblioteca mongoose é atribuir automaticamente o nome da coleção no formato plural, no MongoDB. Caso este comportamento não seja desejado, podemos definir um nome de coleção fixo usando um parâmetro a mais no mongoose.model():
var Blog = mongoose.model('Blog', blogSchema, 'blog')
Os tipos de dados primitivos utilizados pelo mongoose são:
- String
- Number
- Date
- Buffer
- Boolean
- Mixed
- ObjectId
- Array
- Decimal128
- Map
Além disso, também é possivel definir dentro de um
required- booleano ou função, define se o atributo é obrigatóriodefault- define o valor padrão para o atributovalidate- define uma função de validaçãolowercaseeuppercase- booleano, retorna letras modificadas do valorminemax- valida valor máximo e mínimoindexes : { unique : true}- define um atributo único no indice
Mais na Documentação
Para inserir um novo documento na base de dados, primeiro precisamos iniciar um novo objeto a partir do modelo:
var Blog = mongoose.model('Blog', blogSchema)
var post = new Blog({
title: "Olá mundo!",
author: "Ramon Venson",
body: "Seja bem vindo ao mongoose",
})
Em seguida, o objeto post pode ser salvo facilmente usando a função save():
post.save(function(err){
if(!err){
console.log("Objeto salvo com sucesso!")
}
})
Apos ser salvo no banco de dados, o objeto criado recebe o _id localmente e pode ser alterado dentro do contexto local. Ao utilizar a função save() novamente, por exemplo, é possível atualizar o objeto já salvo
//Após realizar o primeiro save() do objeto
console.log(post._id) // Retorna seu _id
post.author = "Rogerinho do Ingá"
post.save() // Atualiza o objeto no banco
Para realizar uma consulta em objetos (documentos), podemos usar:
Blog.find({author: "Ramon Venson"}, function(err, doc){
//Imprime todos os posts encontrados na pesquisa
console.log(doc)
})
Para retornar todas as ocorrencias possíveis, também é possível utilizar a função find apenas com o callback:
Blog.find(function(err, doc){
//Imprime todos os posts
console.log(doc)
})
Podemos realizar a atualização de um documento usando seu _id através da função findByIdAndUpdate(). Essa função aceita atualização parcial de um documento:
Blog.findByIdAndUpdate("5f929234943405679494e465", {title: "Novo Mundo!"}, function(err, doc){
if(!err){
console.log("Objeto Atualizado: " + doc)
}
})
Podemos realizar a deleção de um documento usando seu _id através da função findByIdAndDelete()
Blog.findByIdAndDelete("5f929234943405679494e465", function(err, doc){
if(!err){
console.log("Objeto deletado: " + doc)
}
})
Quando retornamos muitos resultados do banco, é comum realizar paginação para evitar um intenso tráfego de dados na rede e consumo de tempo computacional sem necessidade. Para essa tarefa, podemos atrelar as funções limit(n) e skip(n), que filtra o resultado automaticamente antes que seja enviado ao cliente, como neste exemplo:
Blog.find(function(err, doc){
//Imprime apenas 5 resultados, pulando os 2 primeiros
console.log(doc)
}).limit(5).skip(2)
O mongoose permite atribuir alguns relacionamentos entre coleções. Levamos em consideração os esquemas a seguir:
const JogoSchema = new mongoose.Schema({
nome: String,
desenvolvedora: {
type: mongoose.Schema.Types.ObjectId,
ref: 'desenvolvedoras'
}
})
const DesenvolvedoraSchema = new mongoose.Schema({
nome: String,
pais: String
})
const Jogo = mongoose.model('jogos', JogoSchema)
const Desenvolvedora = mongoose.model('desenvolvedoras', DesenvolvedoraSchema)
Ao utilizar o find() para buscar um jogo, retornariamos o atributo desenvolvedora com o _id único atribuído.
Jogo.find(function (err, result) {
res.json(result)
})
Retorna:
{
"_id": "5d955978dab55639505396d6",
"nome": "Super Mario World",
"desenvolvedora": "5d955cc41f512317f8e3b1d6"
}
No entanto, neste caso, podemos utilizar a função populate() para trazer, automaticamente, os dados da desenvolvedora junto ao jogo.
Jogo.find().populate('desenvolvedora').then(function(doc){
res.json(doc)
})
Retorna:
{
"_id": "5d955978dab55639505396d6",
"nome": "Super Mario World",
"desenvolvedora": {
"nome": "Nintendo",
"pais": "Japão"
}
}
É possível alterar o identificador único gerado pelo MongoDB na criação de um novo documento. Para isso, basta inserir no esquema o atributo _id, que pode ser de qualquer um dos tipos aceitos pelo Mongoose.
Dessa forma, ao usar a função populate(), o mongoose usará o novo identificador, que deve obedecer as seguintes restrições:
- Precisa ser um valor único
- Não pode ser modificado durante o ciclo de vida do documento
Exercícios Complementares
Implemente uma pequena API capaz de manipular os dados de albuns de música. A aplicação deve possuir três endpoints /album realizando as seguintes funções:
- Um método
POSTque recebe um objeto de album através do corpo da mensagem (body) e salva no banco, retornando sucesso ou o erro ao usuário - Um método
GETretorna um vetor contendo todos os albuns cadastrados - Um método
GETque retorna apenas um álbum específico, de acordo com seu id
O schema do álbum deve conter pelo menos os seguintes elementos:
new mongoose.Schema({
nome: String,
artista: String,
ano: Number,
generos: Array,
faixas: Array,
lancamento: Date ou Number
})