Indeks Unik
Termasuk bagian Database dengan MongoDB dalam perjalanan Node.js di Coddy. Pelajaran 9 dari 18.
Dua buku dengan ISBN yang sama tidak boleh disimpan sekaligus, dan melakukan findOne sebelum setiap penyisipan menyisakan celah: dua permintaan bisa sama-sama tidak menemukan apa pun lalu sama-sama menyisipkan data.
Indeks adalah daftar terurut berisi nilai suatu field yang disimpan database di samping collection agar dokumen dapat ditemukan dengan cepat. Indeks unik juga menolak dokumen kedua dengan nilai yang sudah tersimpan: penyisipan akan memunculkan MongoServerError dengan kode 11000, "E11000 duplicate key error".
await users.createIndex({ email: 1 }, { unique: true });
try {
await users.insertOne({ email: 'ada@example.com' });
} catch (err) {
if (err.code !== 11000) throw err;
// email tersebut sudah digunakan
}Buat indeks saat memulai, sebelum data dimasukkan. Membuat indeks yang sudah ada tidak melakukan apa pun.
Tantangan
Mudah- Saat memulai, buat indeks unik pada
isbn. POST /booksdengan ISBN yang sudah tersimpan memberikan respons409, bukan menyimpan salinan kedua.
Kirim POST /books dengan ISBN Dune, 9780441013593. Lalu buka tab Data: Dune ada di sana satu kali.
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.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 });
}
// Mengembalikan :id sebagai ObjectId, atau 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;
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.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');
});
Pelajaran ini berisi kuis singkat. Mulai pelajarannya untuk menjawab kuis dan melacak progresmu.
Semua pelajaran di Database dengan MongoDB
Latihan sendiri: Playground Express