Indeksy unikatowe
Część sekcji Bazy danych z MongoDB ścieżki Node.js w Coddy. Lekcja 17 z 29.
Nie należy przechowywać dwóch książek z tym samym numerem ISBN, a wykonanie findOne przed każdym wstawieniem pozostawia lukę: dwa żądania mogą jednocześnie niczego nie znaleźć i oba wstawić dokument.
Indeks to posortowana lista wartości pola, którą baza danych przechowuje obok kolekcji, aby szybko znajdować dokumenty. Unikalny indeks odrzuca również drugi dokument z wartością, którą już zawiera: wstawienie powoduje zgłoszenie błędu MongoServerError z kodem 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;
// ten adres e-mail jest już zajęty
}Utwórz indeks podczas uruchamiania, zanim zostaną dodane dane. Utworzenie indeksu, który już istnieje, nic nie zmienia.
Wyzwanie
ŁatwyŻadne dwie książki nie mogą mieć tego samego numeru ISBN, nawet jeśli dwa żądania nadejdą w tej samej chwili. Niech baza danych odrzuci duplikat.
- W pliku
server.js, na samej górze, utwórz unikatowy indeks dlaisbn. Zrób to przed zapisaniem przez kod początkowy trzech książek za pomocąinsertManyi zaczekaj na zakończenie operacji, używającawait. - W
POST /booksoperacja wstawiania zgłasza teraz błąd, gdy numer ISBN jest już zapisany. Przechwyć ten błąd. Gdy jegocodema wartość11000, odpowiedz statusem 409 i błędem, na przykład{ "error": "That ISBN is already on the shelf" }. Zgłoś ponownie każdy inny błąd, aby Express odpowiedział statusem 500. - Książka z nowym numerem ISBN nadal otrzymuje status 201 i jest zapisywana, tak jak wcześniej. Wypróbuj: Wyślij
POST /booksz drugą kopią książki Dune:{ "title": "Dune", "author": "Frank Herbert", "year": 1965, "isbn": "9780441013593", "genre": "scifi" }. Otrzymasz status 409 i błąd. WyślijGET /books: nadal otrzymasz trzy książki, a karta Data pokaże Dune tylko raz. WyślijPOST /booksz{ "title": "Beloved", "author": "Toni Morrison", "year": 1987, "isbn": "9781400033416", "genre": "literary" }: otrzymasz status 201.
Spróbuj swoich sił
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());
// Każdy błąd zwraca odpowiedź w postaci { error: message }.
function sendError(res, status, message) {
res.status(status).json({ error: message });
}
// Parametr :id jako ObjectId lub null po zwróceniu odpowiedzi 400 dla nieprawidłowego id.
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');
});
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Bazy danych z MongoDB
1Dokumenty
Książka jako dokumentKolekcje i bazy danychŁączenie z MongoDBOdczytywanie zapisanych dokumentówWstawianie dokumentówWyszukiwanie jednego dokumentuWyszukiwanie według ObjectIdPorównywanie liczbFiltry zapytańWybieranie zwracanych pólSortowanie wyników zapytaniaOgraniczanie do pierwszych wynikówProjekcja, sortowanie i limit2Modyfikowanie dokumentów
Aktualizowanie dokumentówOperatory aktualizacjiPoćwicz samodzielnie: Edytor online Express