Using a private taxonomy can give huge performance gains. I'm going to cover some examples of the things you can do, and the gains to be had.
Setting Up Your Utility Taxonomy
We'll be using this taxonomy to mark posts, but since it won't be public facing, the registration code is quite small:
function tomjn_utility_taxonomy() {
$args = array(
'label' => __( 'Internal Markers', 'tomjn' ),
'public' => false,
'rewrite' => false,
);
register_taxonomy( 'utility', 'post', $args );
}
add_action( 'init', 'tomjn_utility_taxonomy', 0 );
We can now search for posts very quickly by tagging and flagging posts. A lot of developers will use post meta to store this kind of information, but this leads to super expensive post meta queries.
But that's not all that we can do.
Filtering Things Out On Homepages
We're going to use this to make filtering and excluding posts on the homepage obscenely fast. Most users will save a post meta field named hide_from_home
but this is the worst way imaginable to do this for performance. The meta query to filter those posts out can add seconds to your loading time. With enough visitors it can bring down a database server. Adding more posts to your site makes these queries even more expensive, and in a non-linear fashion.
Instead, we want to query for what we actually want, so why not mark all posts with show_on_homepage
on publish? Then we can remove that tag for posts we want to exclude without incurring a performance cost. We can even tag them with a hide_on_homepage
tag so that they can be listed
Automated Post Marking, & The Home Page
So lets automate this. Starting with the publish_post
hook:
function tomjn_auto_add_to_home( $post_id ) {
wp_set_object_terms( $post_id, 'show_on_homepage', 'utility', true );
}
add_action( 'publish_post', 'tomjn_auto_add_to_home' );
Now all posts are set to show on the homepage when published, and we can adjust the homepage query to only show these posts with pre_get_posts
:
function tomjn_only_show_home( $query ) {
if ( $query->is_home() && $query->is_main_query() ) {
$query->set( 'utility', 'show_on_homepage' );
}
}
add_action( 'pre_get_posts', 'tomjn_only_show_home' );
Now all that needs to be done to hide a post from the homepage is to uncheck show_on_homepage
in the edit screen. Other tags can be added for hiding from other areas, or even promoting posts
HomePage Carousels and Feature Sections
Some developers will store an option with a list of post IDs of what to show in the carousels on pages. They might even try to exclude posts from featured areas But this is inflexible!
What if editorial wants complete control of what posts appear in a highlight reel? What if we want more than 1 carousel!
Well, why not use a taxonomy? With a hidden featured_area
taxonomy we can group posts together into featured areas, such as homepage_gallery
, or food_section_highlights
.
Now, whenever you want a post to show in a featured posts section, tag it accordingly.
We can even swap areas and galleries out by storing the term to display in an option, rather than the posts.
Thanks Tom very useful and helpful for the performance 🙂
Great article, and much needed too. I believe this approach was adopted by WooCommerce for their starred products.
I think it may help developers is they had the possibility to display these boolean taxonomies as a predef radio button to users. Maybe add a parameter when registering a taxonomy?
These small UI changes can make a big difference in changing mindsets of both developers and users.
Cheers.
Great! =]
This is a great tip! I see Jetpack is using a private term “featured” (a post tag) for it’s featured posts. And querying posts by taxonomy has great performance.
Hi Tom,
Thanks for the article, I know this is an older thread but I’m curious as to your opinion on the following:
Using a utility taxonomy as a sort of boolean for tax queries, what is the most efficient method for querying?
The taxonomy I’m testing only has one term, named ‘yes’.
Objective: get all the posts which have this term (or any within the utility taxonomy, as this is the only one).
Methods tested:
1)
$tax_query = array(
array(
‘taxonomy’ => ‘utility-taxonomy’,
‘terms’ => ‘yes’,
),
);
2)
$tax_query = array(
array(
‘taxonomy’ => ‘utility-taxonomy’,
‘operator’ => ‘EXISTS’,
),
);
3)
$term_id = get_term_by( ‘name’, ‘yes’, ‘utility-taxonomy’ )->term_id;
$tax_query = array(
array(
‘taxonomy’ => ‘utility-taxonomy’,
‘terms’ => $term_id,
),
);
4) Something else?
I’m not seeing a huge difference in performance between these options, so I’m curious as to what the best method would be.
They’re essentially doing different versions of the same thing, I would not expect any performance difference.
There may be performance differences in more extreme situations, e.g. 500 million posts, but I do not expect them to be meaningful in difference.
I would note however, that using an entire taxonomy for just the word “yes” is not a very good use of a utility taxonomy. The term doesn’t describe what it does, and if you ever needed another boolean value you would have to register another utility taxonomy.
That’s why my example is “show-on-homepage”, not “yes” or “show”.