Mastering Sequelize Search Parameters

Understanding the Problem

We're building a music database API that needs sophisticated search capabilities. Users should be able to search for musicians based on various criteria including names, band affiliations, and instruments played. Think of it like building a music streaming service's search feature where users can find artists through multiple filters.

The main challenges we need to solve are:

  1. Implementing basic search filters for musician properties
  2. Adding search capabilities across related models (bands and instruments)
  3. Supporting flexible field selection
  4. Handling result ordering
  5. Managing pagination of results

Breaking Down the Solution

Let's approach this step by step using the query parameter system:

Basic Query Structure


// Base query object that we'll build upon
let query = {
    where: {},      // For filtering conditions
    include: [],    // For related models
    attributes: [], // For field selection
    order: [],      // For sorting
    limit: 5,       // For pagination
    offset: 0       // For pagination
};
    

Step 1: Basic Musician Filtering


// Handle firstName and lastName filters
if (req.query.firstName) {
    query.where.firstName = req.query.firstName;
}

if (req.query.lastName) {
    query.where.lastName = req.query.lastName;
}
    

Think of this like a filing cabinet where we're looking for folders matching specific names. The WHERE clause acts as our filtering criteria.

Step 2: Band Association Filtering


// Handle bandName filter
if (req.query.bandName) {
    query.include.push({
        model: Band,
        where: { name: req.query.bandName }
    });
}
    

This is similar to looking through related files in a filing system - we're checking a musician's band affiliation before including them in results.

Step 3: Instrument Filtering


// Handle instrumentTypes filter
if (req.query.instrumentTypes) {
    query.include.push({
        model: Instrument,
        where: { type: req.query.instrumentTypes },
        through: { attributes: [] } // Hide join table
    });
}
    

Imagine this as checking a musician's skill set - we're filtering based on what instruments they can play.

Step 4: Field Selection


// Handle musician field selection
if (req.query.musicianFields) {
    if (req.query.musicianFields.includes('all')) {
        delete query.attributes;
    } else if (req.query.musicianFields.includes('none')) {
        query.attributes = [];
    } else {
        query.attributes = req.query.musicianFields;
    }
}
    

This is like choosing which details to show on a musician's profile card - you might want just their name, or all their information.

Step 5: Result Ordering


// Handle result ordering
if (req.query.order) {
    query.order = req.query.order.map(orderItem => {
        const [field, direction] = orderItem.split(',');
        return [field, direction?.toUpperCase() || 'ASC'];
    });
} else {
    query.order = [['lastName'], ['firstName']];
}
    

Think of this as arranging your search results like sorting a deck of cards - you can choose which characteristics determine the order.

Advanced Features and Best Practices

Partial Matching


// Using LIKE for partial matches
if (req.query.bandName) {
    query.include.push({
        model: Band,
        where: {
            name: {
                [Op.like]: `%${req.query.bandName}%`
            }
        }
    });
}
    

Middleware for Common Functions


// Reusable pagination middleware
const paginationMiddleware = (req, res, next) => {
    const { page = 1, size = 5 } = req.query;
    req.pagination = {
        limit: page === '0' ? null : Number(size),
        offset: page === '0' ? 0 : (Number(page) - 1) * Number(size)
    };
    next();
};
    

Common Pitfalls and Solutions

Here are some common issues to watch out for when implementing search:

Performance Optimization Tips

To keep your search feature fast and efficient:

Real World Applications

This pattern of search implementation is common in many applications:

Testing Your Search Implementation

Here are some key test cases to consider: