Query Filters
Part of the Databases with MongoDB section of Coddy's Node.js journey. Lesson 9 of 29.
A client often wants only some books, and the database can do the picking.
The object you pass to find is a filter: a document matches when every field in the filter matches. A plain value means equal. An operator is a key that starts with $ and compares instead: $gte means greater than or equal, $lte less than or equal. Two operators on the same field share one object: { total: { $gte: 20, $lte: 50 } } keeps the totals from 20 to 50.
// paid orders of 20 or more
await orders.find({ status: 'paid', total: { $gte: 20 } }).toArray();Types count: { $gte: '20' } compares a string, and MongoDB never matches a string against a number field. Everything in req.query is a string, so turn numbers into numbers with Number() before they go into a filter.
Challenge
EasyThis step opens the course’s prepared bookshelf API project, rather than your project files from Finding by ObjectId (including server.js). Your earlier work stays in its own lesson. Read the supplied files before editing server.js. This copy contains the bookshelf insertion and ObjectId lookup routes.
Clients want only some books: one author's books, or the books from certain years. Let the database pick them.
- In
server.js, changeGET /books. Build a filter object from the query string and pass it tofind. The route still responds with a JSON array of books. ?author=Jane%20Austenreturns only the books whoseauthoris exactlyJane Austen. (%20is how a URL writes a space.)?from=1960returns the books with ayearof 1960 or later.?to=1980returns the books from 1980 or earlier. With both, a book must fall between the two years, and the years themselves count. Either one can come alone.- Query values always arrive as strings, but the stored years are numbers. Turn
fromandtointo numbers before they go into the filter, or nothing matches. - With no query, the route returns every book, as before. Try it: Send
GET /books?from=1960&to=1980: you get Dune (1965) and Kindred (1979). SendGET /books?from=1970: you get only Kindred. SendGET /books?to=1900: you get only Emma (1815). SendGET /books?author=Jane%20Austen: you get only Emma. SendGET /books: you get all three.
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 list = await books.find().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');
});
All lessons in Databases with MongoDB
Practice on your own: Express playground