{"id":12977,"date":"2026-09-12T07:35:00","date_gmt":"2026-09-12T05:35:00","guid":{"rendered":"https:\/\/www.lukaswojcik.com\/blog\/?p=12977"},"modified":"2026-10-24T06:23:55","modified_gmt":"2026-10-24T04:23:55","slug":"tutorial-finding-a-slow-meta_query-and-rewriting-it-as-a-taxonomy-filter","status":"publish","type":"post","link":"https:\/\/www.lukaswojcik.com\/blog\/en\/wordpress-plugins-tricks\/tutorials-en-wordpress-plugins-tricks\/tutorial-finding-a-slow-meta_query-and-rewriting-it-as-a-taxonomy-filter\/","title":{"rendered":"Tutorial: Finding a Slow meta_query and Rewriting It as a Taxonomy Filter"},"content":{"rendered":"<p>A slow product filter deserves measurement before a data-model change. A <code>meta_query<\/code> can be appropriate; a taxonomy can help categorical filters. Neither the number of joins nor the number of distinct values alone predicts the faster solution. The first requirement is that the rewritten query still selects the same products.<\/p>\n<h2>1. Preserve the complete filter<\/h2>\n<p>The example searches published products with colour <code>blau<\/code>, material <code>eiche<\/code> and availability <code>1<\/code>. The original and rewritten query must keep all three conditions, the same ordering and the same pagination. Availability remains a metadata condition here.<\/p>\n<pre><code>&lt;?php\n$base = [\n    &#x27;post_type&#x27; =&gt; &#x27;produkt&#x27;,\n    &#x27;post_status&#x27; =&gt; &#x27;publish&#x27;,\n    &#x27;posts_per_page&#x27; =&gt; 20,\n    &#x27;paged&#x27; =&gt; 1,\n    &#x27;orderby&#x27; =&gt; [&#x27;date&#x27; =&gt; &#x27;DESC&#x27;, &#x27;ID&#x27; =&gt; &#x27;DESC&#x27;],\n];\n$before = new WP_Query($base + [\n    &#x27;meta_query&#x27; =&gt; [\n        &#x27;relation&#x27; =&gt; &#x27;AND&#x27;,\n        [&#x27;key&#x27; =&gt; &#x27;farbe&#x27;, &#x27;value&#x27; =&gt; &#x27;blau&#x27;],\n        [&#x27;key&#x27; =&gt; &#x27;material&#x27;, &#x27;value&#x27; =&gt; &#x27;eiche&#x27;],\n        [&#x27;key&#x27; =&gt; &#x27;verfuegbar&#x27;, &#x27;value&#x27; =&gt; &#x27;1&#x27;],\n    ],\n]);\n$after = new WP_Query($base + [\n    &#x27;tax_query&#x27; =&gt; [\n        &#x27;relation&#x27; =&gt; &#x27;AND&#x27;,\n        [&#x27;taxonomy&#x27; =&gt; &#x27;farbe&#x27;, &#x27;field&#x27; =&gt; &#x27;slug&#x27;,\n         &#x27;terms&#x27; =&gt; [&#x27;blau&#x27;], &#x27;include_children&#x27; =&gt; false],\n        [&#x27;taxonomy&#x27; =&gt; &#x27;material&#x27;, &#x27;field&#x27; =&gt; &#x27;slug&#x27;,\n         &#x27;terms&#x27; =&gt; [&#x27;eiche&#x27;], &#x27;include_children&#x27; =&gt; false],\n    ],\n    &#x27;meta_query&#x27; =&gt; [\n        [&#x27;key&#x27; =&gt; &#x27;verfuegbar&#x27;, &#x27;value&#x27; =&gt; &#x27;1&#x27;],\n    ],\n]);\n\/\/ SQL: $before-&gt;request and $after-&gt;request\n<\/code><\/pre>\n<p>This code assumes the custom post type <code>produkt<\/code> and both taxonomies already exist and contain the correct assignments. It is not a WooCommerce query. It executes two queries for comparison; the application would use the chosen version. Equality must be checked across all relevant pages and filter combinations, not only the first twenty results.<\/p>\n<figure class=\"lw-diagram\"><img decoding=\"async\" src=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/diagrams\/dia-12977-meta-tax-20261024-en.png\" width=\"1120\" height=\"580\" loading=\"lazy\" alt=\"Preserve filter conditions and verify the migration\"><figcaption>Preserve filter conditions and verify the migration<\/figcaption><\/figure>\n<h2>2. Inspect actual SQL and its plan<\/h2>\n<p><code>$query-&gt;request<\/code> contains the generated SQL. The three positive metadata conditions joined by AND normally require three metadata joins in this example. This is not a universal one-clause\/one-join rule: compatible OR clauses can reuse an alias. Logical table aliases are not physical copies of the database table.<\/p>\n<p>The standard WordPress schema indexes <code>post_id<\/code> and a prefix of <code>meta_key<\/code>, but not <code>meta_value<\/code>. Existing installations may have extra indexes. The optimizer can start from the posts table or another useful access path; it does not invariably scan every row with a particular key. Duplicate metadata rows can expand intermediate results.<\/p>\n<p>For a protected development request, <code>SAVEQUERIES<\/code> records SQL, execution time and calling information. It adds memory and processing overhead and can expose sensitive values in logs. A database slow-query log provides a different perspective and does not supply the same PHP call path. Its activation requires appropriate privileges, an explicit logging period and consideration of volume. Changing a global session-default setting does not update every existing connection.<\/p>\n<p><code>EXPLAIN<\/code> describes an estimated plan. <code>rows<\/code> and <code>filtered<\/code> must be read together with join order, access type and selected indexes. A large estimate alone does not prove that a filter is ineffective. <code>Using filesort<\/code> means an additional sorting operation, not necessarily a disk sort. <code>Using temporary<\/code> is not by itself proof of a bad query. MySQL\u2019s <code>EXPLAIN ANALYZE<\/code> actually executes the statement; MariaDB syntax and behavior must be checked separately.<\/p>\n<p><code>meta_value_num<\/code> is numeric ordering, implemented by WordPress as a numeric expression, not a text comparison. Expressions and large intermediate results can increase work, but the earlier claim that sorting normally costs more than all filters had no measured basis.<\/p>\n<h2>3. Register both categorical attributes<\/h2>\n<pre><code>&lt;?php\nadd_action(&#x27;init&#x27;, function () {\n    foreach ([&#x27;farbe&#x27;, &#x27;material&#x27;] as $taxonomy) {\n        register_taxonomy($taxonomy, [&#x27;produkt&#x27;], [\n            &#x27;hierarchical&#x27; =&gt; false,\n            &#x27;public&#x27; =&gt; false,\n            &#x27;publicly_queryable&#x27; =&gt; false,\n            &#x27;show_ui&#x27; =&gt; true,\n            &#x27;show_admin_column&#x27; =&gt; true,\n            &#x27;show_in_rest&#x27; =&gt; false,\n            &#x27;query_var&#x27; =&gt; false,\n            &#x27;rewrite&#x27; =&gt; false,\n        ]);\n    }\n});\n<\/code><\/pre>\n<p>Both <code>farbe<\/code> and <code>material<\/code> are registered. The example disables public taxonomy queries and rewrite routes, while allowing administration. This is not an authorization boundary. Block-editor access requires an appropriate REST configuration, including <code>show_in_rest<\/code>; REST exposure and permissions need their own decision.<\/p>\n<p><code>term_relationships<\/code> contains object ID, term-taxonomy ID and term order. Its composite primary key starts with the object ID, and a separate index starts with the term-taxonomy ID. These access paths can help exact category matching. A composite index is not automatically equally useful from either end. A boolean flag is not inherently unsuitable for a taxonomy, and publication status should not be repurposed merely to encode stock availability.<\/p>\n<h2>4. Plan migration before writing<\/h2>\n<p>The following <code>plan.php<\/code> is deliberately read-only. It covers two example IDs and explicit value mappings; both lists need adapting to the actual inventory. Target terms must already exist. Unknown, empty, duplicate or structured metadata stops the plan instead of silently losing information. Empty values need an explicit policy: retain an assignment, remove it or flag it for review.<\/p>\n<pre><code>&lt;?php\n\/\/ Read-only example for reviewed IDs, not a complete migration runner.\n$ids = [101, 102];\n$mapping = [\n    &#x27;farbe&#x27; =&gt; [&#x27;blau&#x27; =&gt; &#x27;blau&#x27;, &#x27;rot&#x27; =&gt; &#x27;rot&#x27;],\n    &#x27;material&#x27; =&gt; [&#x27;eiche&#x27; =&gt; &#x27;eiche&#x27;, &#x27;buche&#x27; =&gt; &#x27;buche&#x27;],\n];\n$plan = [];\nforeach ($ids as $id) {\n    if (get_post_type($id) !== &#x27;produkt&#x27;) {\n        throw new RuntimeException(&quot;Unexpected post type: $id&quot;);\n    }\n    foreach ($mapping as $taxonomy =&gt; $values) {\n        if (!taxonomy_exists($taxonomy)) {\n            throw new RuntimeException(&quot;Missing taxonomy: $taxonomy&quot;);\n        }\n        $raw = get_post_meta($id, $taxonomy, false);\n        if (count($raw) !== 1 || !is_string($raw[0])\n            || !array_key_exists($raw[0], $values)) {\n            throw new RuntimeException(&quot;Unmapped or ambiguous value: $id\/$taxonomy&quot;);\n        }\n        $term = get_term_by(&#x27;slug&#x27;, $values[$raw[0]], $taxonomy);\n        if (!$term || is_wp_error($term)) {\n            throw new RuntimeException(&quot;Missing target term: $id\/$taxonomy&quot;);\n        }\n        $old = wp_get_object_terms($id, $taxonomy, [&#x27;fields&#x27; =&gt; &#x27;ids&#x27;]);\n        if (is_wp_error($old)) {\n            throw new RuntimeException($old-&gt;get_error_message());\n        }\n        $plan[] = [\n            &#x27;post_id&#x27; =&gt; $id,\n            &#x27;taxonomy&#x27; =&gt; $taxonomy,\n            &#x27;source_value&#x27; =&gt; $raw[0],\n            &#x27;old_term_ids&#x27; =&gt; array_map(&#x27;intval&#x27;, $old),\n            &#x27;new_term_ids&#x27; =&gt; [(int) $term-&gt;term_id],\n        ];\n    }\n}\necho wp_json_encode($plan, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);\n<\/code><\/pre>\n<p>Execution with <code>wp eval-file plan.php<\/code> prints the proposed assignments. It performs no term assignment or metadata deletion. A complete migration additionally needs bounded batches, a backup of source values and old assignments, a write freeze or synchronization of every writer, and a checked resume strategy.<\/p>\n<p>After the plan has been reviewed and the current source values and old assignments have been compared again, the following API fragment applies one row. Integer term IDs prevent confusion with numeric term names. The false argument replaces assignments within this taxonomy, so existing terms may be removed.<\/p>\n<pre><code>&lt;?php\n\/\/ Fragment for one previously reviewed and revalidated plan row.\n$result = wp_set_object_terms(\n    (int) $row[&#x27;post_id&#x27;],\n    array_map(&#x27;intval&#x27;, $row[&#x27;new_term_ids&#x27;]),\n    $row[&#x27;taxonomy&#x27;],\n    false\n);\nif (is_wp_error($result)) {\n    throw new RuntimeException($result-&gt;get_error_message());\n}\n\/\/ Read back term IDs and compare them with new_term_ids.\n<\/code><\/pre>\n<p>The fragment is not an atomic migration runner. Each write needs read-back verification and error handling; a later failure does not automatically roll back earlier rows. Re-running an old plan after taxonomy edits can overwrite newer work. Automatic slug normalization can merge distinct source values, which is why the mapping is explicit.<\/p>\n<h2>5. Validate behavior before retiring metadata<\/h2>\n<p>Comparisons should include result IDs, ordering, counts, missing values, duplicate metadata, drafts and scheduled content in migration scope, and multilingual assignments where relevant. Query latency should be measured with comparable data, indexes, cache conditions and workload. No reproducible measurement supports the earlier graphic\u2019s 480,000-versus-2,100-row claim.<\/p>\n<p>The old command <code>wp post meta delete --all --keys=farbe --dry-run<\/code> was invalid for this task. The documented command requires an object ID; <code>--all<\/code> means all metadata for that object, and no such dry-run option is documented. Old metadata should remain until writers, comparisons and rollback are resolved. Cleanup needs its own explicit scope and backup.<\/p>\n<div class=\"lw-faq\">\n<h2>Questions and answers<\/h2>\n<h3>When is another data model preferable?<\/h3>\n<p>Numeric ranges may benefit from typed indexed columns or an existing lookup table. Small or selective metadata queries may already be fast. Neither thousands of terms nor a handful of matching rows establishes a universal threshold. Full-text indexes tokenize text and do not automatically support arbitrary substrings.<\/p>\n<h3>What changes for WooCommerce?<\/h3>\n<p>Existing product attributes, visibility rules and lookup tables should be inspected first. WooCommerce\u2019s product type and data APIs differ from this invented example. A parallel custom taxonomy must not bypass stock, visibility, translation or extension behavior.<\/p>\n<\/div>\n<h2>Sources<\/h2>\n<ul>\n<li><a href=\"https:\/\/developer.wordpress.org\/reference\/classes\/wp_meta_query\/find_compatible_table_alias\/\">WP_Meta_Query: compatible aliases<\/a><\/li>\n<li><a href=\"https:\/\/developer.wordpress.org\/reference\/functions\/register_taxonomy\/\">register_taxonomy<\/a><\/li>\n<li><a href=\"https:\/\/developer.wordpress.org\/reference\/functions\/wp_set_object_terms\/\">wp_set_object_terms<\/a><\/li>\n<li><a href=\"https:\/\/developer.wordpress.org\/cli\/commands\/post\/meta\/delete\/\">WP-CLI: post meta delete<\/a><\/li>\n<li><a href=\"https:\/\/dev.mysql.com\/doc\/refman\/8.4\/en\/explain-output.html\">MySQL: EXPLAIN output<\/a><\/li>\n<li><a href=\"https:\/\/dev.mysql.com\/doc\/refman\/8.4\/en\/slow-query-log.html\">MySQL: slow query log<\/a><\/li>\n<li><a href=\"https:\/\/developer.wordpress.org\/advanced-administration\/debug\/debug-wordpress\/\">WordPress: debugging<\/a><\/li>\n<li><a href=\"https:\/\/developer.wordpress.org\/reference\/classes\/wp_query\/\">WP_Query<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>A taxonomy filter can help categorical product searches, provided every original condition survives the rewrite. The tutorial explains SQL inspection, result comparison and read-only migration planning without promising an unmeasured speedup.<\/p>\n","protected":false},"author":1,"featured_media":24148,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[92642],"tags":[91104,91093,91219,91315,91096],"class_list":["post-12977","post","type-post","status-publish","format-standard","hentry","category-tutorials-en-wordpress-plugins-tricks","tag-mysql","tag-php","tag-tutorial","tag-web-performance","tag-wordpress"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Tutorial: Finding a Slow meta_query and Rewriting It as a Taxonomy Filter | Lukas Wojcik<\/title>\n<meta name=\"description\" content=\"Reading the SQL WP_Query builds, measuring it with EXPLAIN, and moving a filter out of wp_postmeta into a taxonomy that MySQL can actually index.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.lukaswojcik.com\/blog\/en\/wordpress-plugins-tricks\/tutorials-en-wordpress-plugins-tricks\/tutorial-finding-a-slow-meta_query-and-rewriting-it-as-a-taxonomy-filter\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Tutorial: Finding a Slow meta_query and Rewriting It as a Taxonomy Filter | Lukas Wojcik\" \/>\n<meta property=\"og:description\" content=\"Reading the SQL WP_Query builds, measuring it with EXPLAIN, and moving a filter out of wp_postmeta into a taxonomy that MySQL can actually index.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.lukaswojcik.com\/blog\/en\/wordpress-plugins-tricks\/tutorials-en-wordpress-plugins-tricks\/tutorial-finding-a-slow-meta_query-and-rewriting-it-as-a-taxonomy-filter\/\" \/>\n<meta property=\"og:site_name\" content=\"Lukas Wojcik - Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-09-12T05:35:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-10-24T04:23:55+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/10\/hero-12977-meta-tax-20261024-dark.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Lukas Wojcik\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Lukas Wojcik\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"8 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/wordpress-plugins-tricks\\\/tutorials-en-wordpress-plugins-tricks\\\/tutorial-finding-a-slow-meta_query-and-rewriting-it-as-a-taxonomy-filter\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/wordpress-plugins-tricks\\\/tutorials-en-wordpress-plugins-tricks\\\/tutorial-finding-a-slow-meta_query-and-rewriting-it-as-a-taxonomy-filter\\\/\"},\"author\":{\"name\":\"Lukas Wojcik\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"headline\":\"Tutorial: Finding a Slow meta_query and Rewriting It as a Taxonomy Filter\",\"datePublished\":\"2026-09-12T05:35:00+00:00\",\"dateModified\":\"2026-10-24T04:23:55+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/wordpress-plugins-tricks\\\/tutorials-en-wordpress-plugins-tricks\\\/tutorial-finding-a-slow-meta_query-and-rewriting-it-as-a-taxonomy-filter\\\/\"},\"wordCount\":960,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"image\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/wordpress-plugins-tricks\\\/tutorials-en-wordpress-plugins-tricks\\\/tutorial-finding-a-slow-meta_query-and-rewriting-it-as-a-taxonomy-filter\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/hero-12977-meta-tax-20261024-dark.png\",\"keywords\":[\"MySQL\",\"PHP\",\"Tutorial\",\"Web Performance\",\"WordPress\"],\"articleSection\":[\"Tutorials\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/wordpress-plugins-tricks\\\/tutorials-en-wordpress-plugins-tricks\\\/tutorial-finding-a-slow-meta_query-and-rewriting-it-as-a-taxonomy-filter\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/wordpress-plugins-tricks\\\/tutorials-en-wordpress-plugins-tricks\\\/tutorial-finding-a-slow-meta_query-and-rewriting-it-as-a-taxonomy-filter\\\/\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/wordpress-plugins-tricks\\\/tutorials-en-wordpress-plugins-tricks\\\/tutorial-finding-a-slow-meta_query-and-rewriting-it-as-a-taxonomy-filter\\\/\",\"name\":\"Tutorial: Finding a Slow meta_query and Rewriting It as a Taxonomy Filter | Lukas Wojcik\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/wordpress-plugins-tricks\\\/tutorials-en-wordpress-plugins-tricks\\\/tutorial-finding-a-slow-meta_query-and-rewriting-it-as-a-taxonomy-filter\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/wordpress-plugins-tricks\\\/tutorials-en-wordpress-plugins-tricks\\\/tutorial-finding-a-slow-meta_query-and-rewriting-it-as-a-taxonomy-filter\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/hero-12977-meta-tax-20261024-dark.png\",\"datePublished\":\"2026-09-12T05:35:00+00:00\",\"dateModified\":\"2026-10-24T04:23:55+00:00\",\"description\":\"Reading the SQL WP_Query builds, measuring it with EXPLAIN, and moving a filter out of wp_postmeta into a taxonomy that MySQL can actually index.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/wordpress-plugins-tricks\\\/tutorials-en-wordpress-plugins-tricks\\\/tutorial-finding-a-slow-meta_query-and-rewriting-it-as-a-taxonomy-filter\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/wordpress-plugins-tricks\\\/tutorials-en-wordpress-plugins-tricks\\\/tutorial-finding-a-slow-meta_query-and-rewriting-it-as-a-taxonomy-filter\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/wordpress-plugins-tricks\\\/tutorials-en-wordpress-plugins-tricks\\\/tutorial-finding-a-slow-meta_query-and-rewriting-it-as-a-taxonomy-filter\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/hero-12977-meta-tax-20261024-dark.png\",\"contentUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/hero-12977-meta-tax-20261024-dark.png\",\"width\":1200,\"height\":630,\"caption\":\"Tutorial: Finding a Slow meta_query and Rewriting It as a Taxonomy Filter\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/wordpress-plugins-tricks\\\/tutorials-en-wordpress-plugins-tricks\\\/tutorial-finding-a-slow-meta_query-and-rewriting-it-as-a-taxonomy-filter\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Tutorial: Finding a Slow meta_query and Rewriting It as a Taxonomy Filter\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\",\"name\":\"Lukas Wojcik - Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\",\"name\":\"Lukas Wojcik\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"contentUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"width\":424,\"height\":636,\"caption\":\"Lukas Wojcik\"},\"logo\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\"},\"sameAs\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Tutorial: Finding a Slow meta_query and Rewriting It as a Taxonomy Filter | Lukas Wojcik","description":"Reading the SQL WP_Query builds, measuring it with EXPLAIN, and moving a filter out of wp_postmeta into a taxonomy that MySQL can actually index.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.lukaswojcik.com\/blog\/en\/wordpress-plugins-tricks\/tutorials-en-wordpress-plugins-tricks\/tutorial-finding-a-slow-meta_query-and-rewriting-it-as-a-taxonomy-filter\/","og_locale":"en_US","og_type":"article","og_title":"Tutorial: Finding a Slow meta_query and Rewriting It as a Taxonomy Filter | Lukas Wojcik","og_description":"Reading the SQL WP_Query builds, measuring it with EXPLAIN, and moving a filter out of wp_postmeta into a taxonomy that MySQL can actually index.","og_url":"https:\/\/www.lukaswojcik.com\/blog\/en\/wordpress-plugins-tricks\/tutorials-en-wordpress-plugins-tricks\/tutorial-finding-a-slow-meta_query-and-rewriting-it-as-a-taxonomy-filter\/","og_site_name":"Lukas Wojcik - Blog","article_published_time":"2026-09-12T05:35:00+00:00","article_modified_time":"2026-10-24T04:23:55+00:00","og_image":[{"width":1200,"height":630,"url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/10\/hero-12977-meta-tax-20261024-dark.png","type":"image\/png"}],"author":"Lukas Wojcik","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Lukas Wojcik","Est. reading time":"8 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/wordpress-plugins-tricks\/tutorials-en-wordpress-plugins-tricks\/tutorial-finding-a-slow-meta_query-and-rewriting-it-as-a-taxonomy-filter\/#article","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/wordpress-plugins-tricks\/tutorials-en-wordpress-plugins-tricks\/tutorial-finding-a-slow-meta_query-and-rewriting-it-as-a-taxonomy-filter\/"},"author":{"name":"Lukas Wojcik","@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"headline":"Tutorial: Finding a Slow meta_query and Rewriting It as a Taxonomy Filter","datePublished":"2026-09-12T05:35:00+00:00","dateModified":"2026-10-24T04:23:55+00:00","mainEntityOfPage":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/wordpress-plugins-tricks\/tutorials-en-wordpress-plugins-tricks\/tutorial-finding-a-slow-meta_query-and-rewriting-it-as-a-taxonomy-filter\/"},"wordCount":960,"commentCount":0,"publisher":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"image":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/wordpress-plugins-tricks\/tutorials-en-wordpress-plugins-tricks\/tutorial-finding-a-slow-meta_query-and-rewriting-it-as-a-taxonomy-filter\/#primaryimage"},"thumbnailUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/10\/hero-12977-meta-tax-20261024-dark.png","keywords":["MySQL","PHP","Tutorial","Web Performance","WordPress"],"articleSection":["Tutorials"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.lukaswojcik.com\/blog\/en\/wordpress-plugins-tricks\/tutorials-en-wordpress-plugins-tricks\/tutorial-finding-a-slow-meta_query-and-rewriting-it-as-a-taxonomy-filter\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/wordpress-plugins-tricks\/tutorials-en-wordpress-plugins-tricks\/tutorial-finding-a-slow-meta_query-and-rewriting-it-as-a-taxonomy-filter\/","url":"https:\/\/www.lukaswojcik.com\/blog\/en\/wordpress-plugins-tricks\/tutorials-en-wordpress-plugins-tricks\/tutorial-finding-a-slow-meta_query-and-rewriting-it-as-a-taxonomy-filter\/","name":"Tutorial: Finding a Slow meta_query and Rewriting It as a Taxonomy Filter | Lukas Wojcik","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/wordpress-plugins-tricks\/tutorials-en-wordpress-plugins-tricks\/tutorial-finding-a-slow-meta_query-and-rewriting-it-as-a-taxonomy-filter\/#primaryimage"},"image":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/wordpress-plugins-tricks\/tutorials-en-wordpress-plugins-tricks\/tutorial-finding-a-slow-meta_query-and-rewriting-it-as-a-taxonomy-filter\/#primaryimage"},"thumbnailUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/10\/hero-12977-meta-tax-20261024-dark.png","datePublished":"2026-09-12T05:35:00+00:00","dateModified":"2026-10-24T04:23:55+00:00","description":"Reading the SQL WP_Query builds, measuring it with EXPLAIN, and moving a filter out of wp_postmeta into a taxonomy that MySQL can actually index.","breadcrumb":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/wordpress-plugins-tricks\/tutorials-en-wordpress-plugins-tricks\/tutorial-finding-a-slow-meta_query-and-rewriting-it-as-a-taxonomy-filter\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.lukaswojcik.com\/blog\/en\/wordpress-plugins-tricks\/tutorials-en-wordpress-plugins-tricks\/tutorial-finding-a-slow-meta_query-and-rewriting-it-as-a-taxonomy-filter\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/wordpress-plugins-tricks\/tutorials-en-wordpress-plugins-tricks\/tutorial-finding-a-slow-meta_query-and-rewriting-it-as-a-taxonomy-filter\/#primaryimage","url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/10\/hero-12977-meta-tax-20261024-dark.png","contentUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/10\/hero-12977-meta-tax-20261024-dark.png","width":1200,"height":630,"caption":"Tutorial: Finding a Slow meta_query and Rewriting It as a Taxonomy Filter"},{"@type":"BreadcrumbList","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/wordpress-plugins-tricks\/tutorials-en-wordpress-plugins-tricks\/tutorial-finding-a-slow-meta_query-and-rewriting-it-as-a-taxonomy-filter\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.lukaswojcik.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Tutorial: Finding a Slow meta_query and Rewriting It as a Taxonomy Filter"}]},{"@type":"WebSite","@id":"https:\/\/www.lukaswojcik.com\/blog\/#website","url":"https:\/\/www.lukaswojcik.com\/blog\/","name":"Lukas Wojcik - Blog","description":"","publisher":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.lukaswojcik.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9","name":"Lukas Wojcik","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","contentUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","width":424,"height":636,"caption":"Lukas Wojcik"},"logo":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg"},"sameAs":["https:\/\/www.lukaswojcik.com\/blog"]}]}},"_links":{"self":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts\/12977","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/comments?post=12977"}],"version-history":[{"count":5,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts\/12977\/revisions"}],"predecessor-version":[{"id":24154,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts\/12977\/revisions\/24154"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media\/24148"}],"wp:attachment":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media?parent=12977"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/categories?post=12977"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/tags?post=12977"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}