Projection, Sort and Limit
Part of the Databases with MongoDB section of Coddy's Node.js journey. Lesson 13 of 29.
A "newest books" list needs a few fields from a few books, in order.
A projection picks the fields a query answers: 1 keeps a field, and _id comes along unless you set it to 0. On the cursor, sort({ field: -1 }) orders from high to low (1 is low to high), and limit(n) keeps the first n results.
// the 5 highest scores, name and score only
await players
.find({}, { projection: { name: 1, score: 1, _id: 0 } })
.sort({ score: -1 })
.limit(5)
.toArray();The database sorts before it limits, in whatever order you chain the two calls. Sorting the array in JavaScript after limit sorts the wrong five.
Challenge
EasyThis step opens the course’s prepared bookshelf API project, rather than your project files from Query Filters (including server.js). Your earlier work stays in its own lesson. Read the supplied files before editing server.js. This copy contains the bookshelf routes and year-range filter.
The home page shows the newest books, with only their title and year.
- In
server.js, add aGET /books/newestroute. Put it aboveGET /books/:id. Express tries routes in order, and/books/:idalso matches/books/newest. It would treatnewestas an id and respond with 400. - The route returns books sorted by
year, highest first. ?n=says how many books to return. Whennis missing, return 3.- Each book holds only
titleandyear, with no_idand no other field. Let the query pick the fields, sort the books and cut the list. For the starter's books,GET /books/newest?n=2returns:Try it: Send[ { "title": "Kindred", "year": 1979 }, { "title": "Dune", "year": 1965 } ]GET /books/newest. You get Kindred (1979), Dune (1965) and Emma (1815), in that order, each with only a title and a year. SendGET /books/newest?n=1: you get[ { "title": "Kindred", "year": 1979 } ]. If you get 400,GET /books/:idtook the request, so move your route above it.
Try it yourself
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());
// Every error answers { error: message }.
function sendError(res, status, message) {
res.status(status).json({ error: message });
}
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/:id', async (req, res) => {
if (!ObjectId.isValid(req.params.id)) {
return sendError(res, 400, 'Not a valid id');
}
const book = await books.findOne({ _id: new ObjectId(req.params.id) });
if (!book) {
return sendError(res, 404, 'No such book');
}
res.json(book);
});
app.listen(3000, () => {
console.log('Bookshelf is running on port 3000');
});
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Databases with MongoDB
Practice on your own: Express playground