Operadores de Atualização
Parte da seção Bancos de dados com MongoDB do Journey de Node.js da Coddy. Lição 15 de 29.
Adicionar um exemplar ou uma etiqueta deve alterar um livro diretamente, sem lê-lo, modificá-lo em JavaScript e gravá-lo de volta.
$inc adiciona um número a um campo, começando em 0 quando o campo não existe. $push acrescenta um valor a um campo de array, criando o array quando ele não existe. Uma atualização pode conter vários operadores:
await players.updateOne(
{ name: 'Ada' },
{ $inc: { wins: 1 }, $push: { badges: 'streak' } }
);O banco de dados aplica a alteração por conta própria, então duas solicitações feitas ao mesmo tempo contam. $set não serve aqui: ele substitui todo o array badges pelo valor que você fornece.
Desafio
FácilA estante conta quantas cópias de cada livro você tem, e os leitores podem adicionar tags aos livros. Ambas as alterações acontecem no banco de dados, em uma única atualização cada, sem ler o livro antes.
- Em
server.js, adicionePOST /books/:id/copies. Não precisa de corpo. Ele adiciona 1 ao campocopiesdo livro. Um livro que ainda não temcopiesfica com"copies": 1. - Adicione
POST /books/:id/tags. O corpo informa uma tag, por exemplo{ "tag": "classic" }. A rota adiciona a tag ao final da listatagsdo livro e mantém as tags que já estavam lá. Um livro que ainda não temtagsfica com"tags": ["classic"]. - Ambas as rotas usam
parseIdpara um id inválido, que resulta em 400. Ambas respondem com 404 quando nenhum livro tem esse_id. Caso contrário, ambas respondem com o status 200 e o livro atualizado completo. Depois de adicionar duas cópias e as tagsclassice depoisfavorite, Dune fica assim:Experimente: Envie{ "_id": "6720a1f4c2b9e81d3a5f0c11", "title": "Dune", "author": "Frank Herbert", "year": 1965, "isbn": "9780441013593", "genre": "scifi", "copies": 2, "tags": ["classic", "favorite"] }GET /bookse copie o_idde Dune. EnviePOST /books/<Dune's _id>/copiescom o corpo vazio: você recebe Dune com"copies": 1. Envie novamente:"copies": 2. EnviePOST /books/<Dune's _id>/tagscom{ "tag": "classic" }:"tags": ["classic"]. Envie novamente com{ "tag": "favorite" }:"tags": ["classic", "favorite"]. EnviePOST /books/000000000000000000000000/copies: você recebe 404.
Experimente você mesmo
import express from 'express';
import { MongoClient, ObjectId } from 'mongodb';
const client = new MongoClient('mongodb://localhost:27017');
const db = client.db('shelf');
const books = db.collection('books');
await books.insertMany([
{ title: 'Dune', author: 'Frank Herbert', year: 1965, isbn: '9780441013593', genre: 'scifi' },
{ title: 'Kindred', author: 'Octavia E. Butler', year: 1979, isbn: '9780807083697', genre: 'scifi' },
{ title: 'Emma', author: 'Jane Austen', year: 1815, isbn: '9780141439587', genre: 'classic' },
]);
const app = express();
app.use(express.json());
// Todo erro retorna { error: message }.
function sendError(res, status, message) {
res.status(status).json({ error: message });
}
// Retorna :id como um ObjectId ou null após responder com 400 para um id inválido.
function parseId(req, res) {
if (ObjectId.isValid(req.params.id)) {
return new ObjectId(req.params.id);
}
sendError(res, 400, 'Not a valid id');
return null;
}
app.get('/', (req, res) => {
res.send('Bookshelf API');
});
app.get('/books', async (req, res) => {
const filter = {};
if (req.query.author) {
filter.author = req.query.author;
}
if (req.query.from || req.query.to) {
filter.year = {};
if (req.query.from) {
filter.year.$gte = Number(req.query.from);
}
if (req.query.to) {
filter.year.$lte = Number(req.query.to);
}
}
const list = await books.find(filter).toArray();
res.json(list);
});
app.post('/books', async (req, res) => {
const book = req.body;
await books.insertOne(book);
res.status(201).json(book);
});
app.get('/books/newest', async (req, res) => {
const n = Number(req.query.n) || 3;
const list = await books
.find({}, { projection: { title: 1, year: 1, _id: 0 } })
.sort({ year: -1 })
.limit(n)
.toArray();
res.json(list);
});
app.get('/books/:id', async (req, res) => {
const id = parseId(req, res);
if (!id) return;
const book = await books.findOne({ _id: id });
if (!book) {
return sendError(res, 404, 'No such book');
}
res.json(book);
});
app.patch('/books/:id', async (req, res) => {
const id = parseId(req, res);
if (!id) return;
const result = await books.updateOne({ _id: id }, { $set: req.body });
if (result.matchedCount === 0) {
return sendError(res, 404, 'No such book');
}
res.json(await books.findOne({ _id: id }));
});
app.listen(3000, () => {
console.log('Bookshelf is running on port 3000');
});
Todas as lições de Bancos de dados com MongoDB
1Documentos
Um livro como documentoColeções e bancos de dadosConectando-se ao MongoDBLendo documentos armazenadosInserindo documentosEncontrando um documentoEncontrando por ObjectIdComparando númerosFiltros de consultaEscolhendo os campos retornadosOrdenando os resultados da consultaMantendo os primeiros resultadosProjeção, ordenação e limite2Alterando Documentos
Atualizando DocumentosOperadores de AtualizaçãoPratique por conta própria: Playground de Express