Menu
CoddyTech

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 icon

Challenge

Easy

This 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.

  1. In server.js, change GET /books. Build a filter object from the query string and pass it to find. The route still responds with a JSON array of books.
  2. ?author=Jane%20Austen returns only the books whose author is exactly Jane Austen. (%20 is how a URL writes a space.)
  3. ?from=1960 returns the books with a year of 1960 or later. ?to=1980 returns 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.
  4. Query values always arrive as strings, but the stored years are numbers. Turn from and to into numbers before they go into the filter, or nothing matches.
  5. 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). Send GET /books?from=1970: you get only Kindred. Send GET /books?to=1900: you get only Emma (1815). Send GET /books?author=Jane%20Austen: you get only Emma. Send GET /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