Think of a library catalog system. When you're looking for a book, you might want to search by the author's first name, last name, or perhaps filter to see only hardcover books. In our student management system, we need to create similar filtering capabilities. We need to allow users to:
Search for students by:
These filters should work together with our existing pagination and be efficient in terms of database queries.
Let's build this step by step, starting with our enhanced students endpoint:
const express = require('express');
const { Op } = require('sequelize');
const { Student } = require('../../db/models');
router.get('/', async (req, res) => {
// Destructure all query parameters
let { page, size, firstName, lastName, lefty } = req.query;
// Initialize pagination params with defaults
page = parseInt(page) || 1;
size = parseInt(size) || 10;
// Initialize the where clause for filtering
const where = {};
// Build firstName filter
if (firstName) {
where.firstName = {
[Op.iLike]: `%${firstName}%` // Case-insensitive pattern matching
};
}
// Build lastName filter
if (lastName) {
where.lastName = {
[Op.iLike]: `%${lastName}%`
};
}
// Build handedness filter
if (lefty) {
if (lefty !== 'true' && lefty !== 'false') {
return res.status(400).json({
errors: [{ message: 'Lefty should be either true or false' }],
count: 0,
pageCount: 0
});
}
where.leftHanded = lefty === 'true';
}
try {
// Get filtered count for pagination
const totalCount = await Student.count({ where });
// Calculate pagination values
const pageCount = Math.ceil(totalCount / size);
const offset = (page - 1) * size;
// Build query options
const queryOptions = {
where,
order: [
['lastName', 'ASC'],
['firstName', 'ASC']
]
};
// Add pagination unless it's the special case
if (page !== 0 || size !== 0) {
queryOptions.limit = size;
queryOptions.offset = offset;
}
// Execute the main query
const students = await Student.findAll(queryOptions);
// Return formatted response
return res.json({
rows: students,
page: page || 1,
pageCount,
count: totalCount
});
} catch (err) {
console.error(err);
return res.status(500).json({
errors: [{ message: 'Server error occurred' }],
count: 0,
pageCount: 0
});
}
});
Let's break down how pattern matching works with the % wildcard:
'%dexter%' matches: "Dexter", "Dexterday", "MrDexter"
'R%y' matches: "Ray", "Ruby", "Randy"
'ay' matches: "Ray", "May", "Jay"
Search filters are everywhere in modern applications:
We use iLike instead of Like to make searches case-insensitive. This means:
"Dexter" matches: "dexter", "DEXTER", "DeXtEr"
When multiple filters are applied, they work as AND conditions:
firstName: "Ray"
lastName: "Smith"
lefty: true
// This will find left-handed students named Ray Smith
Test the following scenarios:
Common errors to handle:
To optimize filter performance:
When troubleshooting filters:
Consider these potential improvements: