I have a simple WP_Query:

$loop = new WP_Query(array( 
      'post_type' => array('image', 'video'), 
      'category_name' => $cat, 
      'orderby'=>'date meta_value_num'

How can I alter this so that it return only videos with the tag editor-pick while still returning all images regardless of tags?

I'm looking for a filter to add onto the MySQL query so that order and pagination remain intact.

  • Where is this query happening? A widget?
    – Tom J Nowell
    Commented Dec 3, 2014 at 1:50
  • Yes, you're going to want to use a query filter, along with some more advanced sql queries to achieve this goal. Just using the WP_Query object won't achieve this alone. Commented Dec 3, 2014 at 4:56
  • 1
    I'm sure @tf is pretty close to solve this for you with his solution (+1), but in the meanwhile I wonder if this kind of experiment can help you?
    – birgire
    Commented Dec 30, 2014 at 19:03
  • @birgire, that's exactly what I need! Thanks so much for putting together that plugin!
    – jetlej
    Commented Dec 30, 2014 at 23:59
  • 1
    The reason I wrote that it wasn't supported is that in general one sub-query might not contain any meta query, so ordering by a meta value might then not make sense. We must order by a field that's contained in both (all) sub-queries. I just played with it and found a way to order the combined query (where all the sub-queries contain a meta query) by a meta value, I will update that plugin version (plus some bug fixes) on Github in couple of days. Then we could continue on the Issue tracker over there.
    – birgire
    Commented Dec 31, 2014 at 11:00

5 Answers 5


I happened to be working on something similar today and just remembered you were looking for something like this, so let me share it with you.

Here's how one can use the posts_where filter to restrict the WP_Query, to posts in some custom post type cpt1 OR another post type cpt2 that's attached to some taxonomy term.

Here's an example:

add_filter( 'posts_where', 'wpse_posts_where' );
$loop = new WP_Query( $args );


 * Restrict WP_Query to ( cpt1 OR cpt2 attached to a given term in some taxonomy).
 * @see http://wordpress.stackexchange.com/a/173889/26350

function wpse_posts_where( $where )
    global $wpdb;

    // Run this filter callback only once:
    remove_filter( current_filter(), __FUNCTION__ );

    // Modify this to your needs:  
    $cpt1       = 'image';     
    $cpt2       = 'video';    
    $taxonomy   = 'post_tag';     // Related to cpt2
    $term_slug  = 'editor-pick';  // Related to cpt2    

    // Get the term info for the term_taxonomy_id:
    $term = get_term_by( 'slug', $term_slug, $taxonomy );

    // Modify the SQL query:
    if( ! is_wp_error( $term ) )
        $where .= " AND ( {$wpdb->posts}.post_type = '{$cpt1}' 
            OR {$wpdb->posts}.post_type = '{$cpt2}' 
            AND {$wpdb->posts}.post_status = 'publish'
            AND {$wpdb->posts}.ID IN (
                SELECT object_id FROM {$wpdb->term_relationships} 
                WHERE term_taxonomy_id IN ( {$term->term_taxonomy_id} ) ) ) ";
    return $where;

where you might have to modify it further to your needs.

  • 1
    You are a god! Thanks so much! One note for people perusing this answer in the future, you would have to still ask for both images and videos in the WP_Query args ($args).
    – jetlej
    Commented Jan 4, 2015 at 9:00
  • This is really really awesome!!!! Commented Apr 17, 2018 at 11:07

Unfortunately, you can't. At least not with a single WP_Query, that is. What you could do, however, is run two separate queries - one for each post type - then combine the results. Like so:

$args = array( 
    'post_type'     => 'image',
    'category_name' => $cat,
    'meta_key'      => 'total_votes',
    'orderby'       => 'date meta_value_num',
$loop = new WP_Query( $args );

$args = array( 
    'post_type'     => 'video',
    'category_name' => $cat,
    'tag'           => 'editor-pick',
    'meta_key'      => 'total_votes',
    'orderby'       => 'date meta_value_num',
$loop2 = new WP_Query( $args );

$loop->posts = array_merge( $loop->posts, $loop2->posts );
$loop->found_posts += $loop2->found_posts;
$loop->max_num_pages = ceil( $loop->found_posts / $loop->query_vars[ 'posts_per_pages' ] );

Note: This is untested code. I hope it may be of help to you, though.

  • I'd prefer to use a filter to add onto the MySQL query so that order and pagination are not messed with.
    – jetlej
    Commented Dec 3, 2014 at 0:16
// tell WordPress about our new query var

function wpse52480_query_vars( $query_vars ){   
    $query_vars[] = 'my_special_query';
    return $query_vars;
add_filter( 'query_vars', 'wpse52480_query_vars' );

// check if our query var is set in any query

function wpse52480_pre_get_posts( $query ){    
    if( isset( $query->query_vars['my_special_query'] ) )

        // do special stuff

    return $query;
add_action( 'pre_get_posts', 'wpse52480_pre_get_posts' );

and in the template:

// set the query var (along with whatever others) to trigger the filter

$args = array(
    'my_special_query' => true
$my_secondary_loop = new WP_Query( $args );

You can use the taxonomy params to achieve this https://codex.wordpress.org/Class_Reference/WP_Query#Taxonomy_Parameters

  • 2
    Can you explain a little bit more.
    – bravokeyl
    Commented Oct 13, 2016 at 18:21

One WP_Query-way of doing this is to fetch all other categories than the ones you want to use, and use operator => NOT IN like so:

$terms_to_use = [ 10, 12, 15 ];

$all_other_terms = get_terms( [
    'taxonomy' => 'category',
    'fields' => 'slugs',
    'exclude' => $terms_to_use,
] );

$args = [
    'post_type' => [ 'post', 'my_cpt' ],
    'tax_query' => [
            'taxonomy' => 'category',
            'operator' => 'NOT IN',
            'terms' => $all_other_terms

This way, you will only get posts with categories 10, 12 and 15, and you will get all posts from my_cpt.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.