The WooCommerce product attributes lookup table, wc_product_attributes_lookup, takes the heavy term join out of the catalog query. It does not take the click off the server. An AJAX filter still boots PHP on every filter action, reads that table, and counts the options again. InstantFilter does not read this table. After the first page load the browser filters on its own index, in 1.5 ms to 5 ms, with 0 SQL.
What is the WooCommerce product attributes lookup table?
Since WooCommerce 6.3, WooCommerce builds a separate table for filtering by attributes. The announcement is on the WooCommerce Developer Blog of 2 February 2022: a migration creates the table, a scheduled action fills it, and the setting “Use the product attributes lookup table for catalog filtering” turns it on for the shop. When that setting is off, filtering falls back to the posts tables, as it did before this table existed.
The table is named wp_wc_product_attributes_lookup. WooCommerce describes it as denormalized data for catalog filtering. You manage it with WP-CLI: wp wc palt can enable it, disable it, inspect it, and rebuild it. wp wc palt regenerate fills it in one run, without waiting on Action Scheduler. regenerate_for_product does that for one product and its variations. That is in the CLI documentation and in the optimization note of 20 June 2024.
If you are searching for woocommerce product attributes lookup table slow or for the name wc_product_attributes_lookup, you are in this layer. The table is WooCommerce’s own fix for slow attribute filters. It is not a bug. It is a precomputed relationship between product and term, so MySQL does not have to invent the same taxonomy join on every click.
Why is wc_product_attributes_lookup still slow at tens of thousands of products?
The table speeds up “which products have the term red”. It does not remove the architecture in which every click asks the server. The browser still posts to admin-ajax.php or to a REST route. WordPress boots. PHP reads wc_product_attributes_lookup. The filter plugin then counts how many products remain for each option. That count is a second query, often per facet.
On a catalog of 10,000, 20,000, or 50,000 products the table itself gets large. Every variation with an attribute is a row. Regenerating, through WooCommerce → Status → Tools or through wp wc palt regenerate, runs in batches and can take a long time on a busy shop. The 2024 Developer Blog describes that regeneration because the table needs maintenance on large catalogs. A slow regenerate is an admin problem. A slow click is a different problem: the request still exists.
Updating the table directly on every product change, the option WooCommerce named in 6.3, keeps the table in step with the catalog. It does not make the filter click local. The shopper still pays for the PHP boot and the round trip. On hosting where PHP workers are scarce, a sale fills the queue even when the SQL is shorter than the old join on wp_term_relationships.
How does this table relate to wp_postmeta?
The previous generation of the same problem is the post on slow filters and wp_postmeta. Global attributes live as terms. Local attributes and many price or stock fields live in postmeta. Filter plugins that join those on every click get slow as the catalog grows. The lookup table is WooCommerce’s answer to the attribute join, not to postmeta, and not to the request.
A shop can have both at once. Attribute filters read wc_product_attributes_lookup. Price, stock, or a custom field still reads meta. Query Monitor then shows a fast lookup and, in the same admin-ajax.php call, a slow meta query. Turning the table on does not remove that second query. You see it because the click still takes hundreds of milliseconds to a few seconds after a successful regenerate.
Does InstantFilter read the product attributes lookup table?
No. During indexing InstantFilter builds its own index and exports a compressed codebook. The indexing documentation describes that file: the browser downloads it after the server render, and later clicks resolve in memory, without the database. WooCommerce can keep the lookup table enabled for other parts of WooCommerce. InstantFilter’s filter click does not go through it.
That distinction has to stay in the sentence. InstantFilter is not a layer on top of wc_product_attributes_lookup. It replaces the question “count this archive again on the server” with an index that was already counted before the shopper clicks. The first HTML stays server-rendered, so the category remains indexable. The interaction after that costs 0 SQL.
When can you tell the lookup table did not fix the filter?
Three checks. One: WooCommerce → Status → Tools shows the lookup table has been built, and the catalog-filtering setting is on. Two: the Network panel still shows a request on a click. Three: Query Monitor or the slow query log names wc_product_attributes_lookup inside that request, next to postmeta if that is also involved. When one and two are both true, the official fix moved the join and left the architecture in place.
The hub behind this is the guide on why filters get slow. This page is the more specific screen: the table WooCommerce added, which on a large catalog still does not stop every click from booting PHP. Regenerating is worth doing when the table is empty or stale. It is not an answer to one request per click.
Facet counts remain the multiplier. One product query plus a count per option is, even on a tidy lookup table, a burst of SQL inside the same worker. Twenty visible colors can mean twenty counts, unless the plugin packs them into one query. That query gets longer as the table grows. The shopper waits on the last number, not on the first product card.
What do you change when the lookup table is on and the click is still slow?
Leave the table on for WooCommerce. Move the filter click off the server. After hydration InstantFilter counts the options in the browser, on the products of the current archive. That click sends no SELECT to wc_product_attributes_lookup. The 800 ms to 2,500 ms of an AJAX round trip drops away, because the round trip is gone. The interaction sits in 1.5 ms to 5 ms.
Test the two side by side on staging. Note whether the lookup table is enabled, click an attribute, and read the request time. Then put the archive on InstantFilter and click the same attribute. The slow query log should not contain that second click. A catalog of tens of thousands of products makes the difference visible. The number is the test, not the title of the page.
How do you see in Query Monitor that the lookup table still serves the click?
Turn Query Monitor on in a staging clone, open a category with many attributes, and click one facet. In that request’s query list, search for wc_product_attributes_lookup. If the table is there, the active filter is using WooCommerce’s official index. Then count the queries in the same request. One SELECT for the products plus a run of COUNTs for the remaining options is normal for an AJAX facet, even when the join itself sits neatly on the lookup table.
Write down the duration of the whole request, not only the duration of the fastest SELECT. The shopper waits for the last byte. A 40 ms lookup followed by 600 ms of counts is a slow click. Compare that with a second click after the archive is on InstantFilter: that second click should not appear as a row in Query Monitor, because there is no PHP request. The table can stay in the database. It should not stay in the shopper’s request.
Keep going
The lookup table is one layer. These pages sit beside it or above it.
- When regeneration never finishes at all
- When the count is zero because of a WPML translation gap
- The newer Blocks counting endpoint, and its own cap
- The previous generation: wp_postmeta
- The request: admin-ajax.php on every click
- Why WooCommerce filters get slow
- AJAX versus frontend filtering
- Pricing and trial