Upsert
Termasuk bagian Database dengan MongoDB dalam perjalanan Node.js di Coddy. Pelajaran 10 dari 18.
Setiap pembaca memberi rating pada sebuah buku satu kali. Jika memberi rating lagi, jumlah bintangnya harus berubah, bukan menambahkan rating kedua.
Dengan { upsert: true }, updateOne memperbarui dokumen yang cocok dengan filter, dan jika tidak ada yang cocok, fungsi ini menyisipkan dokumen yang dibuat dari field filter ditambah pembaruan. Satu pemanggilan menangani pemanggilan pertama dan semua pemanggilan setelahnya.
// satu dokumen pengaturan untuk setiap pengguna
await settings.updateOne(
{ user: 'ada' },
{ $set: { theme: 'dark' } },
{ upsert: true }
);Filter menentukan hal yang membuat sebuah dokumen unik, dalam contoh ini, satu dokumen per pengguna. upsertedId pada hasil akan ditetapkan saat pemanggilan menyisipkan dokumen. insertOne biasa tidak bisa melakukan ini: perintah tersebut menambahkan dokumen setiap kali.
Tantangan
Mudah- Collection
ratingsmenyimpan{ bookId, user, stars }, denganbookIdsebagai ObjectId buku. PUT /books/:id/ratingsdengan{ "user": "ada", "stars": 4 }menyimpan satu rating per pengguna untuk setiap buku dan memberikan respons200dengan{ user, stars }.GET /books/:id/ratingsmenampilkan daftar rating buku tersebut.
Beri rating pada sebuah buku dua kali sebagai ada, lalu tampilkan daftarnya.
Coba sendiri
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.createIndex({ isbn: 1 }, { unique: true });
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());
// Setiap error mengembalikan { error: message }.
function sendError(res, status, message) {
res.status(status).json({ error: message });
}
// Ubah :id menjadi ObjectId, atau kembalikan null setelah mengirim respons 400 jika id tidak valid.
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;
try {
await books.insertOne(book);
} catch (err) {
if (err.code === 11000) {
return sendError(res, 409, 'That ISBN is already on the shelf');
}
throw err;
}
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.post('/books/:id/copies', async (req, res) => {
const id = parseId(req, res);
if (!id) return;
const result = await books.updateOne({ _id: id }, { $inc: { copies: 1 } });
if (result.matchedCount === 0) {
return sendError(res, 404, 'No such book');
}
res.json(await books.findOne({ _id: id }));
});
app.post('/books/:id/tags', async (req, res) => {
const id = parseId(req, res);
if (!id) return;
const result = await books.updateOne({ _id: id }, { $push: { tags: req.body.tag } });
if (result.matchedCount === 0) {
return sendError(res, 404, 'No such book');
}
res.json(await books.findOne({ _id: id }));
});
app.delete('/books/:id', async (req, res) => {
const id = parseId(req, res);
if (!id) return;
const result = await books.deleteOne({ _id: id });
if (result.deletedCount === 0) {
return sendError(res, 404, 'No such book');
}
res.status(204).end();
});
app.listen(3000, () => {
console.log('Bookshelf is running on port 3000');
});
Semua pelajaran di Database dengan MongoDB
Latihan sendiri: Playground Express