I have a simple filter running on the main query on the front page, it should be excluding all posts with a particular meta value (by only including posts who do not have the meta_key set, or those whose meta_key value does not match what we're excluding). But, for whatever reason, it is not behaving as expected.
/**
* Hide posts from main query on front page.
*
* @since 1.0.0
*
* @param object $query WP Query object.
* @return object Modified WP Query object.
*/
function wpse_exclude_posts_from_main_query( $query ) {
// Make sure this only runs on the main query on the front page
if ( is_front_page() && $query->is_main_query() ) {
// Exclude posts that have been explicitly set to hidden
$query->set('meta_query', array(
'relation' => 'OR',
// Include posts where the meta key isn't set
array(
'key' => '_wpse_custom_key',
'value' => 'asdf', // A value must exist due to https://core.trac.wordpress.org/ticket/23268
'compare' => 'NOT EXISTS',
),
// Include posts where the meta key isn't explicitly true
array(
'key' => '_wpse_custom_key',
'value' => true,
'compare' => '!=',
),
) );
}
}
add_action( 'pre_get_posts', 'wpse_exclude_posts_from_main_query' );
Each half of this meta query work perfectly fine on their own. I can either see all posts where the key does not exist, or all posts where the key does exist and is not true. When used in conjunction, as shown above, I only see posts where the key does exist and is not true (the NOT EXISTS portion is ignored completely).
Here is the SQL being generated (according to the posts_request filter):
SELECT SQL_CALC_FOUND_ROWS wp_posts.*
FROM wp_posts
LEFT JOIN wp_postmeta
ON (
wp_posts.ID = wp_postmeta.post_id
AND wp_postmeta.meta_key = '_wpse_custom_key'
)
INNER JOIN wp_postmeta AS mt1
ON (wp_posts.ID = mt1.post_id)
WHERE 1=1
AND wp_posts.post_type = 'post'
AND wp_posts.post_status = 'publish'
AND (
wp_postmeta.post_id IS NULL
OR (
mt1.meta_key = '_wpse_custom_key'
AND CAST(mt1.meta_value AS CHAR) != '1'
)
)
GROUP BY wp_posts.ID
ORDER BY wp_posts.post_date DESC
LIMIT 0, 10
You can see that when it gets down to the meta pieces it is indeed using OR in the WHERE clause, rather than AND as the referenced bug highlights: https://core.trac.wordpress.org/ticket/23268
Hopefully someone can provide some much-needed insight, because I'm completely flummoxed.
_wpse_custom_key
meta value at least one other meta value? Otherwise the second, inner join won't find them, because it requires at least one meta value, and it can't be_wpse_custom_key
.