FacetWP performance, speed, limits and scalability
We often get questions about FacetWP’s scalability and limitations, the speed of indexing, filtering and search, and how to fix slow page loads or slow filtering.
Below we will go into all the different factors that can be at play for indexing, filtering and search:
Indexing speed and limits
As a general guideline, on average FacetWP’s indexer will run at about 700-1000 database rows per minute when indexing WooCommerce products with product variations (a particularly heavy type of content to index).
Indexing speed factors
A lot of factors weigh into the time it takes FacetWP to index your content. Besides the number of post types, the number of posts, custom fields and terms/categories, other factors are the number of facets, the types of the facets being indexed, and the server specs.
Indexed data is stored in FacetWP’s facetwp_index
database table. A row in this table consists of the data of one facet choice for a specific facet, for a specific post ID. So a combination of many post types, many posts (and product variations), many facets, and many facet choices can quickly add up to a huge number of rows. FacetWP and its index table are highly optimized to handle large amounts of content. But there are limits to servers, the database, and MySQL itself to take into account.
See this explanation of how the indexing process works and which/how facet data is stored in the facetwp_index
table.
Improve indexing speed
To improve the time it takes to re-index, besides improving your server speed, think about how you can limit the number of database rows to be indexed:
- Index only the post types you need.
- Reduce the number of post IDs to index. This also includes the number of WooCommerce product variations, because each variation needs to be indexed separately.
- Disable FacetWP’s “Support product variations” setting if you are not using variable products on your facet page.
- Disable FacetWP’s “Index out-of-stock products” setting if you are not displaying out-of-stock products on your facet page.
- Reduce the number of facets.
- Reduce the number of facet choices, for example by reducing the number of terms/categories in taxonomies that facets use as data source.
- Reduce the time it takes to look up each facet’s data source field. For example by checking if you can improve custom code used in the facetwp_index_row hook.
- Trigger the indexer programmatically, for example only after importing posts.
- Trigger the indexer with WP-Cli, with fine control over what exactly is being indexed (which post types, post IDs or facets), if needed on a server cron schedule.
- Trigger the indexer with a cron schedule, letting it run only in quiet times, e.g. at night.
Index only the post types you need
If your website has a lot of content, indexing can take a long time. One simple way of speeding things up is to use the facetwp_indexer_query_args hook to limit indexing to the post types you are actually using with FacetWP.
Even if your facets don’t apply to all post types that exist, FacetWP will still look up every post from every indexable post type, and check it against each facet to see if it needs to be indexed. So removing post types that are not used with facets can potentially speed up indexing.
To check which post types FacetWP currently indexes, click the “Show indexable post types” button in the settings, as shown in the image on the right.
With the facetwp_indexer_query_args
hook, you can specify which post types to index. Or you can prevent specific post types from being indexed. Changes you make with this hook will be reflected when you click the “Show indexable post types” button again.
Index programmatically
FacetWP offers many ways to trigger the indexer programmatically, so you have much more control over what is being indexed, and when.
FacetWP’s automatic indexing can be disabled, making it possible to only index when you want. For example after importing new or updated posts.
Index with WP-Cli
If you index with the index button, FacetWP indexes everything. If you want to limit what is indexed, you can also use WP-Cli. This is the recommended approach for sites with a lot of content that changes often, in which case FacetWP’s automatic indexing can become problematic.
With WP-Cli you can do partial indexes, by specifying the post type(s), post ID(s) or facet(s) to index.
Besides entering WP-Cli commands manually in the server command line, you can also use a server cron job to trigger the WP-Cli indexing commands at pre-defined moments.
If you go for indexing with WP-CLI, it is recommended to turn off automatic indexing.
Index on a cron schedule
You can also use a cron schedule to determine the exact time the indexer runs. This makes it possible to let the indexer for example run at night or at times the server is not busy.
The indexing cron schedule can be run on WP Cron, with the help of the Schedule Indexer add-on, or on a server cron with WP-Cli.
Also in this case it is recommended to turn off automatic indexing.
Indexing limits and scalability
We’ve been tweaking FacetWP’s database index table over the years, and its current form is as efficient as it can get with MySQL. Keep in mind that MySQL is a general-purpose database so in terms of performance it’s not going to compare well with dedicated search solutions like Elasticsearch or Algolia.
The best-case maximum limit of FacetWP would probably be somewhere in the hundreds of thousands of posts. We’ve had a customer successfully testing the plugin with 500K+ results, but the reliable maximum depends a lot on the server specs and the other factors mentioned above.
When reading these numbers be aware that if you are using WooCommerce product variations, the number of indexed posts will be a lot higher than you’d guess. Each variation is a separate item that needs to be indexed, so the true count will be much higher than the number of products.
Indexing value limits
When indexing facets into its wp_facetwp_index
table, and displaying facets, FacetWP uses the following limits:
Value | Maximum | Description |
---|---|---|
facet_name |
50 characters | The facet’s technical name. The name is also visible in the URL when the facet has active selections. |
facet_value |
50 characters | The facet choice’s technical value, see explanation below. |
facet_display_value |
200 characters | The facet choice’s display value, see explanation below. |
post_id |
4294967295 | The maximum post ID. See below for an explanation, and how to increase this number. |
facet choices | unlimited or 1000 | The maximum number of facet choices in facets with a “Count” setting is as high as you set it. If your page/server can handle it, you can set it as high as you like. However, very high counts can kill the loading of the page just from all the HTML this produces. For this reason, if the “Count” is set to -1 , the number of choices is limited to 1000 . This applies to the following facet types: Checkboxes, Dropdown, Radio, fSelect, Hierarchy, and Color facets. |
term depth | 50 levels | The maximum number of term levels indexed when a facet uses a hierarchical taxonomy as data source. |
The indexing of facetwp_value and facetwp_display_value
FacetWP indexes two values for each facet choice. The facetwp_value
is a facet choice’s technical value. It can be seen in the URL when the choice is selected. The facetwp_display_value
is the choice’s label as displayed in the facet itself.
Depending on the data source of the facet, and the value itself, these two values can be the same or different. For simple custom fields they are the same. But for example for taxonomy terms they are different: term slugs are indexed as facetwp_value
, and term names as facetwp_display_value
.
Also, depending on the presence of special characters, the facetwp_value
value can be hashed for security reasons.
Indexing issues
If you have a very large site and the indexer is stalling, make sure to check your web server’s PHP error log for clues. The most common cause is the server running out of memory. See this page for all other common indexing issues.
Indexing and WP Engine
If you are hosting your website on the WP Engine platform and you are experience problems with the indexer stalling or not indexing all your posts, be aware that WP Engine is limiting long queries (longer than 1024 characters) for performance reasons. The following line in wp-config.php will prevent that:
How to use custom PHP code?
PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info
define( 'WPE_GOVERNOR', false );
Fix indexing issues with very high post IDs
FacetWP uses INT unsigned
as the SQL data type for the post_id
column in the wp_facetwp_index
table. This SQL data type can contain 8-byte integer data, which means the highest post ID that can be indexed is 4294967295
(2^32-1
).
Believe it or not, in our support we regularly encounter users with post IDs higher than this. If that happens, those posts will not be indexed properly, which will lead to empty facets or facets with choices missing, depending on if all or some post IDs are above this limit.
Fortunately, there is an easy fix. Add the following code to your (child) theme’s functions.php to change the data type for the post_id
column from INT unsigned
to BIGINT unsigned
:
How to use custom PHP code?
PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info
add_filter( 'facetwp_use_bigint', '__return_true' );
The BIGINT unsigned
data type can hold integers up to 18446744073709551615
(2^64-1
), which should be enough even in the most extreme scenarios.
After adding the above code, make sure to re-create the indexing table by purging it first, and then performing a full-re-index, by clicking the red “Re-index” button.
Website and facet filtering speed
FacetWP was built with performance in mind and is very fast itself. We’ve structured and highly optimized our data storage and have analyzed every query to make sure that it runs as efficiently as possible.
So ultimately, FacetWP’s speed depends on your server, the theme setup, your plugins, and the amount of content. If your server is overloaded (i.e. too many heavy plugins or other users) or underpowered (too little memory or CPU power), then obviously FacetWP will not run at its peak.
Check out our tutorial on how to make your website faster, that covers many of these subjects.
Causes for slow page loads and filtering
To understand where slowness on your listing page may come from, keep in mind that FacetWP can be only as fast as your uncached site. This is because FacetWP uses AJAX requests to refresh the facets and results listing. WordPress AJAX requests aren’t cached by default and require all of WordPress to load. FacetWP uses WP’s init
hook to ensure that WP (and all other plugins) are loaded. Otherwise, no integrations would work, post types wouldn’t be registered, etc. This means that if it takes three seconds for the initial unfiltered (uncached) page to load, then it will also take three seconds for a filtered AJAX request.
The effect of this is that if you have any type of page caching (like WP Rocket) enabled, the initial (unfiltered) cached page load may be faster than the AJAX filtering after interacting with facets, sorting, or pagination. Page caching works by serving pages directly from the cache, as pure HTML, CSS, and Javascript. The database is never queried in this process (except the first time to generate the cached page itself), so serving these cached pages is very fast. But caching plugins like WP Rocket (intentionally) do not cache AJAX requests. So FacetWP’s subsequent AJAX requests (for filtering, pagination, sorting and loading the updated results) have to query the database on each page load, and will show all inefficiencies that may be present in that process.
If you see filtering being slower than the initial (or other) cached page loads, it’s tempting to come to the conclusion that slow filtering or pagination is caused by FacetWP. Although possible, this is very unlikely. Most often it is due to (a combination of) server performance, inefficient listing templates or loop code with large amounts of queries to render each post, the amount of content, or other plugins slowing the AJAX request. Even on a fast server, FacetWP can only be as fast as the slowest plugin on your site.
Certain types of caching can help to speed things up, but if you don’t resolve the underlying issues, you are just sweeping all inefficiencies “under the rug” and they will keep slowing down each AJAX request.
The best course of action if you want to speed up your page and facet filtering is to first (temporarily) disable all caching, and start analyzing your true (uncached) page load times and queries to find out where the slowness is coming from. The recommended tool to do this is the free Query Monitor plugin.
Check out our tutorial about how you can use Query Monitor to optimize performance by reducing the total query time and the number of queries on your page, optimizing the code of your listing template and loops, profiling your plugins, and optimizing your server setup.
A lot of performance can usually be gained by profiling the plugins you are using, de-activating unnecessary or heavy plugins, and making sure the remaining ones work well together. Also make sure to de-activate any plugins you are not continuously using, like WP All Import, database optimization plugins, and Query Monitor itself.
Besides caching and optimizing your queries, the list of things you can do to speed up your website and page loads is near endless. Check out our separate tutorial on improving page load speed to give you an overview and get you started.
Also consider using FacetWP’s built-in Listing Builder for your post listing/grid. On WP archive pages, FacetWP makes a custom AJAX request to the same page URL, but the Listing Builder uses its own REST API endpoint, which is faster. It also loads less HTML in its AJAX call than FacetWP does on a WP archive (see below).
If you want page load times in the ~1s range, the best approach would be to code your own theme and listing archive with as little overhead as possible. Or maybe start with a skeleton theme of some sort, then use our FacetWP’s built-in Listing Builder for your listing template.
Speed up facet filtering itself
After optimizing your theme, plugins, and server specs to speed up page load times, there are a few things you can do to speed up the filtering itself.
Factors that influence filtering speed include:
- the listing template itself (how efficient is the loop and display of each result):
- the number of active facets on the page
- the facet types in use
- caching of FacetWP’s AJAX requests
Use the Listing Builder for your listing template
Using a Listing Builder template is usually faster than using FacetWP on WP archive pages or with a custom query. The Listing Builder is optimized to makes as few SQL queries as possible. On WP archive queries, FacetWP has to load all HTML within the page’s <body>
tag (using AJAX), then parse the response HTML using JavaScript to get the necessary data. The Listing Builder uses its own REST API endpoint and only returns the HTML for the actual listing, which is always less than the whole page, so it is more efficient in that regard too.
Reduce the total number of posts
To get accurate facet counts, FacetWP needs to use the unpaginated query. So you could consider reducing the total number of results in your listing query. If you have a lot of content, you could split it up into different listings/archives, or offer content in separate category/term archive pages.
Reduce the number of posts per page
Try to minimize the number of posts per page in your listing to something reasonable, like 16-20 posts. This will reduce the number of queries needed to load the data for all displayed posts.
Reduce the number of queries per post
Minimize the number of queries needed to display all information for a post. Even if you have only one unnecessary query taking only milliseconds, because of the number of times it runs (the number of posts per page) it can have a – sometimes huge – negative impact on the resulting total query time, especially if your number of posts per page is high.
Reduce the number of facets
Each facet will slow down the page slightly because the choices have to be queried and calculated. We’ve seen users with an enormous amount of facets on a page. Try to limit the amount by going with a few well-chosen facet types tailored to your type of content and your users’ needs.
Choose efficient facet types
Another thing to consider is the choice of facet types. Most facet types are comparable in speed, but a few facet types will be faster than others to load. For example, an Autocomplete facet doesn’t need to calculate its choices on page load, so that’s fewer initial queries. The same is true for Date Range, Number Range, etc. Proximity facets used to be the slowest by far, but recent changes made it a lot faster.
Cache FacetWP’s AJAX requests
Filtering can be sped up by installing the FacetWP Caching add-on. It caches FacetWP’s own AJAX requests for each used filter combination in the database. See below for more info and its limitations.
Website and server optimization
As explained above, FacetWP’s filtering speed depends on how fast your uncached pages load. Important factors in this are the hardware and software your site is running on, your theme and plugins, and the efficiency of your template code. But the list of other things that you can do to increase performance is near endless. We wrote a separate tuturial on how to improve you page load speed to give an overview and get you started.
There are many tools and plugins you can use to debug and optimize your site’s code and analyze page loading times to determine the effectiveness of improvements you make. The recommended tool to do this is the free Query Monitor plugin.
Check out our tutorial about how you can use Query Monitor to optimize performance by optimizing the code and (number of) queries generated by your theme, listing template and loops, profiling your plugins, and optimizing your server setup.
Caching
One of the things that can help speed up your site enormously is to enable one or more caching solutions. But only some of the server-side caching solutions (like OPcache and object caching) will speed up FacetWP filtering.
If you have a high-traffic site, CDN caching can also help a lot in terms of caching all static assets (like images). With a CDN, images will load super fast around the world, and it will save a lot of bandwidth.
Check our tutorial about performance for an introduction to several types of server- and page caching and other things you can do to improve page load speed.
Using caching plugins
There are many WordPress caching/optimization plugins that each offer a range of different caching solutions, often combined with code optimization features like script minification, concatenation and deferring, image optimization, CDN image storage solutions, etc.
FacetWP has built-in support for WP Rocket, a popular (paid) caching plugin. And a lot of our users are using Cloudflare with the Cloudflare plugin.
If you’re using a caching plugin with FacetWP, in general, make sure that HTML minification and script deferring is disabled. HTML minification is often problematic because – depending on the settings – it removes HTML comments, including the <!--fwp-loop-->
comments that FacetWP uses to automatically detect the query results. This often happens when users turn on Cloudflare’s Auto Minify feature. Sometimes you can exclude HTML comment from the minification process, which could prevent this (but test to make sure FacetWP is still working correctly). Script deferring is often problematic because of script timing issues.
If you are using the Pods plugin (which has built-in caching), experiment with the Pods Alternative Cache add-on plugin, especially if you are hosting your site on WP Engine.
The FacetWP Caching add-on
For extra filtering speed you can enable FacetWP’s Caching add-on, which caches FacetWP’s own AJAX requests in the database. The Caching add-on can help speed up high-traffic pages by preventing FacetWP from having to calculate the choices for each facet on each page load.
Each unique facet filter combination that is used will be cached and retrieved from the cache the next time someone chooses that combination. If you have many users selecting a lot of different unique combinations (e.g. entering their own location or making random assortments of facet selections), then caching the AJAX requests isn’t going to help much because those combinations will not have been cached yet. So the Caching add-on will only be effective on frequently visited pages (like landing pages) with only a few facets and facet options/combinations that are used often.
The FacetWP Caching add-on doesn’t cache anything else than its own AJAX requests, so it will not negatively affect other caching systems you are using. It can be used side-by-side with other caching plugins, like WP Rocket, because most of them intentionally ignore AJAX requests.
Depending on how often your content changes, you may want to experiment with the cache expiration time. By default, the cache expires after 1 hour (3600 seconds). If your content only changes weekly or monthly, it would make sense to increase the cache lifetime as well. If your content rarely changes you could set the cache to a really long expiration time, then manually clear it when necessary. That’s actually what we do on facetwp.com because our demo content almost never changes.
Search performance, limits and alternatives
FacetWP – on a WP search page and by extension in Search facets – uses WP Core search, which is extremely basic in functionality. It searches only titles and the content and excerpt fields for matches, and has no understanding of relevancy.
FacetWP’s main limitation in search speed is MySQL itself. Ultimately, MySQL is a general-purpose database and wasn’t built with the intention of being a scalable search engine.
SearchWP
FacetWP has built-in integration with SearchWP, which is much more flexible than WP Core search. It can search all your custom fields and has lots of features to fine-tune the relevancy of the returned results. It can even search pdfs and documents in your Media Libary.
For large sites, keep in mind that SearchWP has a much lower maximum post limit than FacetWP itself because all the advanced features and extra calculations SearchWP is doing behind the scenes require more server resources. SearchWP works best on a site with anywhere from a few posts to a few thousand. If your website has tens of thousands of posts or even hundreds of thousands, SearchWP does recommend not using it (see their FAQ).
Relevanssi
FacetWP has an add-on integration with the Relevanssi search plugin. Relevanssi greatly improves the quality and relevancy of search results by maintaining its own search index in the database.
Be aware that using Relevanssi may require large amounts (hundreds of MBs) of database space (for a reasonable estimate, multiply the size of your wp_posts database table by three). If your hosting setup has a limited amount of space for database tables, using Relevanssi may cause problems. The following information is from the Relevanssi site:
“The upper limit of how big a site Relevanssi can support depends on your hardware. On shared hosting accounts with limited resources, tens of thousands of posts can be too much. If your hardware is solid, especially your database, there’s probably no upper boundary. The biggest site we’ve heard run Relevanssi without problems had two million posts, using a dedicated database server with SSD drives.”
Alternative search solutions
If you are expecting to have hundreds of thousands or even millions of results then you may want to look into Elasticsearch or Algolia, which FacetWP both doesn’t integrate with. These solutions offload search functionality to an external server that’s specialized in handling those searches, which will help speed up your site search.
Elasticsearch with the Elasticpress plugin
The Elasticpress plugin works with Elasticsearch too. This is a complicated setup to get running, see this tutorial for an overview.
Note that FacetWP does not fully integrate with ElasticPress. We have not tested ElasticPress with FacetWP ourselves, but some users have been able to get it working with Search facets specifically, for example with this script. But it does not work with other facet types which still require MySQL (the WordPress database) in order to function properly.
Also note that Elasticpress limits results to 10000 for performance reasons, so FacetWP will also not index or show more than 10000 results. See our Elasticpress page for a way to fix this, and tips on how to get Elasticpress working with FacetWP Search facets.
Algolia
Algolia is a great service – we use it too on this site for the Help Center search. But there is no way to integrate Algolia with FacetWP and we have currently no plans to build this.
Algolia is very fast, mainly because all of its databases are in-memory. The drawback is that it’s expensive. Their pricing used to be more reasonable (and they even had their own in-house WordPress plugin), but years back they shifted focus towards enterprise clients.