How To

Stopwords and Sort Queries

Rejiggering the sort order to ignore specific stop words.

Table of Contents

The code I use is part and parcel from a comment Pascal Birchler made in 2015 and Birgir E. riffed on in 2016. I made one small change.

The Problem The Problem

People like to name TV shows with ‘The’ or ‘A’ or ‘An’ as the first word. “The Fall” and “The Good Wife” for example. However, when we order such things in a human sensible way, “A Touch of Cloth” should be listed behind both of those.

  • Frankenstein
  • The Fall
  • Grey’s Anatomy
  • The Good Wife
  • A Touch of Cloth

WordPress, though, sees them as absolutes and you get this:

  • A Touch of Cloth
  • Frankenstein
  • Grey’s Anatomy
  • The Fall
  • The Good Wife

Not quite right, is it?

Top ↑

The Fix The Fix

This code does two things.

The first part is the filter on the posts_orderby function. That checks if the post type is the one I want to filter (in my case, only shows), and if so, use regex to filter out my stop words of ‘the ‘, ‘an ‘, and ‘a ‘. The extra space in each word is important. I want to reorder “The Fall” and not “Then They Fall” after all!

The second part is the actual filter on the title, to mess with it only for the ordering of posts.

add_filter( 'posts_orderby', function( $orderby, \WP_Query $q ) {
    if( 'post_type_shows' !== $q->get( 'post_type' ) )
        return $orderby;

    global $wpdb;

    // Adjust this to your needs:
    $matches = [ 'the ', 'an ', 'a ' ];

    return sprintf(
        " %s %s ",
        MYSITE_shows_posts_orderby_sql( $matches, " LOWER( {$wpdb->posts}.post_title) " ),
        'ASC' === strtoupper( $q->get( 'order' ) ) ? 'ASC' : 'DESC'

}, 10, 2 );

function MYSITE_shows_posts_orderby_sql( &$matches, $sql )
    if( empty( $matches ) || ! is_array( $matches ) )
        return $sql;

    $sql = sprintf( " TRIM( LEADING '%s' FROM ( %s ) ) ", $matches[0], $sql );
    array_shift( $matches );
    return MYSITE_shows_posts_orderby_sql( $matches, $sql );

If you’re using MariaDB, this can be even easier, but I have to test on my dev site, which uses MySQL.