Assume there is a post type called profilepost that I wanna hide from all kinds of archive. (but not changing the post status to private) I tried to get all post ID that belongs to this post type. The code below failed.

global $wpdb;
$profilepost_ids = $wpdb->get_col( "SELECT ID FROM wp_posts where post_type = 'profilepost'" );
function my_custom_get_posts( $query ) {
    if ( is_admin() || ! $query->is_main_query() ) return;
    if ( $query->is_archive() ) {$query->set( 'post__not_in', $profilepost_ids );}
add_action( 'pre_get_posts', 'my_custom_get_posts', 9999 );

But when I replaced $query->set( 'post__not_in', $profilepost_ids ); with $query->set( 'post__not_in', array(1234,4321) );, where 1234 and 4321 are post IDs, it works.

Someone can tell me what I did wrong here ?

  • 2
    did you print out profilepost_ids? What does that give you? – rudtek Nov 1 '17 at 18:05

pre_get_posts lets you change what a query is meant to retrieve, but what you've done is essentially:

  • Grab posts of type A, B, C, then remove all the posts with these IDs that just happen to be all the posts of type B

When what your end result should be is:

  • Grab posts of type A, C

So why not just do that? First lets retrieve the list of post types to fetch:

$post_types = $query->get( 'post_type' );

Then it's just a matter of removing profilepost from that array, then calling set:

$query->set( 'post_type' $post_types );

So to recap:

  • grab the list of post types in a new variable
  • modify the new list
  • put the new list back into the query

As the middle step is generic PHP, I won't cover it, and instead refer you to this Stack Overflow question.

A Note on post__not_in

This is a stupendously slow thing to do. Avoid anything with NOT or __not_in like the plague. Even if it runs quickly on a single page load, it will give you major scaling issues, and get exponentially slower/more expensive as you add more content

Always query for what you're looking for, never for what you don't want.

A note on using SQL to fetch all posts, and Querying for Things

Always use WP_Query to fetch posts, your SQL query always runs, with no caching to speed things up. What's more, the query is not scalable. This is because it returns everything with no paging. This might work well when there are 5 or 6 posts in the table, but you'll run into issues when there are 100 or 200.

Remember, the posts table contains more than just posts and pages, menu items are posts too, revisions, media attachments, and plenty of hidden post types used for performance and internal reasons that the user never sees. Also keep in mind that your database server and PHP only have so much time and memory to hold things in.

You want small cacheable things you can request lots of, not single large queries that require heavy lifting. This is why setting a max limit is always a good idea even if you never expect to reach it, it sets the worst case scenario.

It's also why NOT IN queries and random orders are terrible as the DB has to scan the entire table to build a new temporary table with all the things you didn't want removed, only then can it run the actual query. Then there's the cleanup afterwards.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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