Using meta_query and custom fields within pre_get_posts to return posts within a numerical range

I’m trying to build a search page for music tracks that lets visitors:

  • Choose the post type
  • Choose a genre
  • Choose a mood
  • Specify a Beats Per Minute (bpm) range

wp_dropdown_categories() is working for the genres and moods it seems (i.e. I don’t need to modify pre_get_posts), but I can’t limit posts to only those within a specific ‘bpm’ range.

The code I’ve written below either returns no results at all if I select certain ranges like 20 for ‘bpm_start’ and 180 for ‘bpm_finish’ or I get strange results for searches like this:

http://www.domain.com/?s=&post_type=ibm_tracks&ibm_genres=ambient-music&ibm_moods=0&bpm_start=20&bpm_finish=180

That should return a bunch of ibm_tracks posts from the ambient-music genre and within a bpm custom field that is between 10 and 180, but it’s returning no posts for some reason…as a side note, no mood was specified in the example above, but choosing a mood doesn’t affect the result anyway, no posts are returned either way.

Can anyone see what I’m doing wrong?

Thanks,

Osu

Form

<?php 
function osu_list_terms_dd($taxonomy_type, $taxonomy, $showalltext) {
    $args = array(
        'taxonomy'          => $taxonomy_type,
        'hide_empty'        => true,
        'name'              => $taxonomy_type,
        'value_field'       => 'slug',
        'show_option_all'   => $showalltext,
        'echo'              => 0,
        //'show_count'      => 1, // Useful for checking how many tracks are in a taxonomy term
        'id'                => 'taxonomy-filter__select-' . $taxonomy,
        'orderby'           => 'name',
        'order'             => 'ASC'
    );
    return wp_dropdown_categories($args);
}
?>

<form role="search" method="get" class="search-form" action="<?php echo esc_url( home_url( "https://wordpress.stackexchange.com/" ) ); ?>">

    <p>Search keywords:</p>
    <input type="search" value="<?php echo get_search_query(); ?>" name="s" />

    <p>Choose post type</p>
    <select name="post_type">
        <option value="ibm_tracks">TRACKS</option>
        <option value="ibm_logos">LOGOS</option>
    </select>

    <p>Choose genre</p>
    <?php echo osu_list_terms_dd('genres', 'ibm_genres', '- choose a genre -'); ?>

    <p>Choose Mood</p>
    <?php echo osu_list_terms_dd('moods', 'ibm_moods', '- choose a mood -'); ?>

    <p><em>BPM start</em></p>
    <select name="bpm_start">
        <option value="0">0</option>
        <option value="10">10</option>
        <option value="20">20</option>
        ... etc...
        <option value="220">220</option>
        <option value="230">230</option>
        <option value="240">240</option>
    </select>

    <p><em>BPM end</em></p>
    <select name="bpm_finish">
        <option value="0">0</option>
        <option value="10">10</option>
        <option value="20">20</option>
        ... etc...
        <option value="220">220</option>
        <option value="230">230</option>
        <option value="240">240</option>
    </select>

</form>

functions.php

// ADD CUSTOM QUERY VAR'S SO BPM WORKS BELOW
function osu_add_query_vars_filter( $vars ){
    $vars[] = "bpm_start";
    $vars[] = "bpm_finish";
    return $vars;
}
add_filter( 'query_vars', 'osu_add_query_vars_filter' );

// MODIFY THE MAIN QUERY OBJECT FOR TRACKS AND LOGOS CUSTOM POST TYPES
function osu_search_filter( $query ) {

    // POST ARCHIVE PAGE : Only for IBM tracks and Logos on search page
    if ( !is_admin() && isset($query->query_vars['post_type']) && is_search() ) {

        // Set vars
        $post_type  = $_GET['post_type'];
        $ibm_tracks="ibm_tracks";
        $ibm_logos="ibm_logos";

        if(isset($post_type)) {

            // Only retrieve ibm_tracks
            // ------------------------------------------------------------------           
            if($post_type == $ibm_tracks) {

                // Set post type to ibm_tracks
                $query->query_vars['post_type'] = $ibm_tracks;

                // Allow url to alter the query
                $bpm_start  = $_GET['bpm_start'];
                $bpm_finish = $_GET['bpm_finish'];

                // Filter posts by BPM range
                if(isset($bpm_start) && isset($bpm_finish)) {

                    // Alter query with meta_query for BPM
                    $meta_query = array(
                        array(
                            'key'       => 'bpm',
                            'value'     => array($bpm_start, $bpm_finish),
                            'type'      => 'NUMERICAL',
                            'compare'   => 'BETWEEN'
                        )
                    );
                    $query->set( 'meta_query', $meta_query );

                }

            } // end if($post_type == ibm_tracks)

            // Only retrieve ibm_logos
            // ------------------------------------------------------------------
            if($post_type == $ibm_logos) {

                // Set post type to ibm_logos
                $query->query_vars['post_type'] = $ibm_logos;

            }

        } // end if(isset($post_type))

    } // end if ( !is_admin() etc. )

    return $query;

}
// Hook my above function to the pre_get_posts action
add_action( 'pre_get_posts', 'osu_search_filter' );

1 Answer
1

The type argument in your meta query is wrong. It spells NUMERICAL but the correct value is NUMERIC.

$meta_query = array(
    array(
        'key'       => 'bpm',
        'value'     => array($bpm_start, $bpm_finish),
        'type'      => 'NUMERIC',
        'compare'   => 'BETWEEN'
    )
);

Leave a Comment