Upsert
Termasuk bagian Database dengan MongoDB dalam perjalanan Node.js di Coddy. Pelajaran 18 dari 29.
Setiap pembaca memberi nilai pada buku satu kali. Jika memberi nilai lagi, bintangnya harus berubah, bukan menambahkan nilai kedua.
Dengan { upsert: true }, updateOne memperbarui dokumen yang cocok dengan filter, dan jika tidak ada yang cocok, metode ini menyisipkan dokumen berdasarkan bidang filter ditambah pembaruan. Satu pemanggilan mencakup kali pertama dan setiap kali sesudahnya.
// satu dokumen pengaturan untuk setiap pengguna
await settings.updateOne(
{ user: 'ada' },
{ $set: { theme: 'dark' } },
{ upsert: true }
);Filter menentukan hal yang membuat dokumen unik, dalam contoh ini, satu dokumen per pengguna. upsertedId pada hasil akan berisi nilai jika pemanggilan tersebut menyisipkan dokumen. insertOne biasa tidak bisa melakukan ini: metode itu menambahkan dokumen setiap kali.
Tantangan
MudahPembaca memberi nilai buku dari 1 sampai 5 bintang. Setiap pembaca hanya memberi nilai untuk sebuah buku satu kali: jika memberi nilai lagi, jumlah bintangnya berubah, bukan menambahkan nilai kedua.
- Di
server.js, di sebelahconst books = db.collection('books');, buka koleksi kedua bernamaratings. Setiap penilaian berupa satu dokumen:Simpan{ "bookId": "6720a1f4c2b9e81d3a5f0c11", "user": "ada", "stars": 4 }bookIdsebagai ObjectId yang dikembalikan olehparseId, bukan sebagai teks dari URL. Dalam JSON, keduanya terlihat sama. - Tambahkan
PUT /books/:id/ratings. Bodynya terlihat seperti{ "user": "ada", "stars": 4 }. Jika pengguna ini sudah memberi nilai untuk buku ini, ubah jumlah bintangnya. Jika belum, tambahkan penilaian baru. Lakukan keduanya dengan satu panggilan. Berikan respons dengan status 200 dan penilaiannya:{ "user": "ada", "stars": 4 }. - Tambahkan
GET /books/:id/ratings. Responsnya berstatus 200 dan berupa array berisi penilaian buku tersebut seperti yang disimpan. Penilaian untuk buku lain tidak boleh muncul:[ { "_id": "6720a1f4c2b9e81d3a5f0c21", "bookId": "6720a1f4c2b9e81d3a5f0c11", "user": "ada", "stars": 2 } ] - Kedua rute menggunakan
parseIduntuk id yang tidak valid, sehingga menghasilkan 400. Coba: KirimGET /booksdan salin_idmilik Dune. KirimPUT /books/<Dune's _id>/ratingsdengan{ "user": "ada", "stars": 4 }: kamu mendapatkan 200 dan{ "user": "ada", "stars": 4 }. Kirim lagi dengan{ "user": "ada", "stars": 2 }: kamu mendapatkan 200 dan{ "user": "ada", "stars": 2 }. KirimGET /books/<Dune's _id>/ratings: kamu mendapatkan satu penilaian milik Ada, dengan 2 bintang. Beri nilai Dune dengan{ "user": "bo", "stars": 5 }lalu tampilkan daftarnya lagi: kamu mendapatkan dua penilaian. Tab Data sekarang menampilkan koleksiratings.GET /books/<Kindred's _id>/ratingsmengembalikan[].
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 });
}
// Kembalikan :id sebagai ObjectId, atau null setelah mengirim respons 400 untuk id yang 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
1Dokumen
Buku sebagai DokumenKoleksi dan DatabaseMenghubungkan ke MongoDBMembaca Dokumen TersimpanMenyisipkan DokumenMenemukan Satu DokumenMenemukan Dokumen berdasarkan ObjectIdMembandingkan AngkaFilter QueryMemilih Field yang DikembalikanMengurutkan Hasil QueryMempertahankan Hasil PertamaProyeksi, Pengurutan, dan BatasLatihan sendiri: Playground Express