How to hide specific post type from archive?

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.