WordPress: Exclude posts with meta_query – Not every posts has the meta_field

I want to exclude every post with a specific value of a custom meta field.
The problem is, that not every posts has this meta field.

My code looks like this (excerpt of the working loop):

Read More
// WP_Query arguments
        $args = array (
            'post_parent'   => $parentid,
            'orderby'       => 'menu_order',
            'order'         => 'ASC',
            'post_type'     => array( 'page' ),
            'meta_query' => array(
                array(
                    'key' => 'hide',
                    'value' => 1,
                    'compare' => '!='
                )
            )
        );

Not every posts uses the field “hide”. Some posts giving back a NULL.
So I think, the loop isn’t working because of that?!

Is this correct? Is it necessary that every posts has a value for that key?

Related posts

3 comments

  1. Another way to do it:

    // WP_Query arguments
        $args = array (
        'post_parent'   => $parentid,
        'orderby'       => 'menu_order',
        'order'         => 'ASC',
        'post_type'     => array( 'page' ),
        'meta_query' => array('0' => array('key' => 'hide', 'value' => '1', 'compare' => 'NOT EXISTS')
        )
    );
    
  2. This is an old post, but to answer it anyways. The meta query in this question will only return results of posts that have that meta key. To also return posts that do not have that meta key at all, you need an additional meta query. Example:

    // WP_Query arguments
    $args = array (
        'post_parent'   => $parentid,
        'orderby'       => 'menu_order',
        'order'         => 'ASC',
        'post_type'     => array( 'page' ),
        'meta_query'    => array(
            'relation'  => 'OR',
            array(
                'key'       => 'hide',
                'value'     => 1,
                'compare'   => '!='
            ),
            array(
                'key'       => 'hide',
                'compare'   => 'NOT EXISTS'
            )
        )
    );
    

    Note the use of “relation” and the second meta query which has the compare value of ‘NOT EXISTS’.

    UPDATE WITH HOW I HANDLE THIS SITUATION PRESENTLY:

    I seem to run into situations like this more and more, and I’ve developed a method for handling it which results in a much quicker SQL query. Whenever a post in the post type in question is saved, I update a list of post IDs that meet my query criteria and store it in a WP option. Then when my query is run, I get this list, and either put it in the ‘include’ or ‘exclude’ query parameter depending on if this is a whitelist or a blacklist.

    This does add a bit of code but the benefits here are both in performance, and also removing some complexity if you have other meta queries that also need to run. Some example code below for a blacklist, this could be adapted for a whitelist as well.

    class Example_Meta_Blacklist
    {
    
        public function init()
        {
            /*
             * Trigger whenever post is saved, you could also trigger on save_post_{$post->post_type}
             * If you are using Advanced Custom Fields, you may need to use acf/save_post
             */
            add_action( 'save_post', [ $this, 'update_my_custom_post_type_blacklist' ] );
    
            /*
             *  One time use function to create an initial list if needed. Remove if not.
             */
            add_action( 'wp', [ $this, 'create_initial_my_custom_post_type_blacklist' ] );
        }
    
        /*
         *  Add or remove post ids from our list whenever the post is saved
         */
        public function update_my_custom_post_type_blacklist( $post_id )
        {
            if ( 'my_custom_post_type' != get_post_type( $post_id ) ) {
                return;
            }
    
            $my_custom_post_type_blacklist  = get_option( 'my_custom_post_type_blacklist', [] );
            $key                            = array_search( $post_id, $my_custom_post_type_blacklist );
            $meta_key_in_question           = get_post_meta( $post_id, 'meta_key_in_question', true );
    
            if ( ! $meta_key_in_question && $key !== false ) {
                unset( $my_custom_post_type_blacklist[ $key ] );
            } else if ( $meta_key_in_question && $key === false ) {
                $my_custom_post_type_blacklist[] = $post_id;
            }
    
            update_option( 'my_custom_post_type_blacklist', $my_custom_post_type_blacklist, true );
        }
    
        /*
         *  When I run into this issue, there are usually already some existing
         *  posts, so I need to run this code one-time to create my initial list
         *  This code would be run by visiting your site with ?gocode=myuniquegocode
         *  at the end of the URL. The function and action hook can then be removed.
         */
        public function create_initial_my_custom_post_type_blacklist()
        {
            if ( ! isset( $_GET['gocode'] ) || 'myuniquegocode' != $_GET['gocode'] ) {
                return;
            }
    
            $clients = get_posts([
                'posts_per_page'    => -1,
                'post_type'         => 'my_custom_post_type',
                'post_status'       => 'publish',
                'fields'            => 'ids',
                'meta_key'          => 'the_meta_key_in_question',
                'meta_value'        => 'the_meta_value_in_question',
                'meta_compare'      => '='
            ]);
    
            update_option( 'my_custom_post_type_blacklist', $clients, true );
        }
    
    }
    
    add_action( 'plugins_loaded', [ new Example_Meta_Blacklist, 'init' ] );
    

    Then in a query for posts:

    $my_query = new WP_Query( [
        'post_type'     => 'my_custom_post_type',
        'post_status'   => 'publish',
        'exclude'       => get_option( 'my_custom_post_type_blacklist', [] )
    ] );
    
  3. Try to check your SQL Statement by doing like below snippet.

    $customPosts = new WP_Query($yourArgs);
    echo "Last SQL-Query: {$customPosts->request}";
    

Comments are closed.