vendredi 25 septembre 2026

À propos

Contact

E-commerce

Antipatterns meta_query sur les produits WooCommerce à fort catalogue

Une requête meta_query qui filtre correctement sur 200 produits peut mettre une boutique à genoux sur 40 000 références. Repérer ce piège avant qu'il ne coûte cher en temps de chargement.

Par Clément Hadrot • 22 septembre 2021 • 5 min de lecture • Aucun commentaire
Antipatterns meta_query sur les produits WooCommerce à fort catalogue

Un catalogue qui démarre à quelques centaines de produits tolère à peu près n’importe quelle requête, y compris une meta_query peu optimisée sur plusieurs critères combinés. Le problème survient plus tard, quand le catalogue dépasse plusieurs dizaines de milliers de références et que cette même requête, jamais revue depuis, commence à ralentir visiblement le chargement des pages de catégorie ou des filtres de recherche. Ce billet ne traite pas le cache d’objet, une couche qui peut masquer temporairement le problème sans le résoudre à la source.

Ce qu’on voit sur le terrain

Le motif récurrent ressemble à ceci : une page de catégorie qui filtre les produits en promotion, en stock, et dans une fourchette de prix, construit une requête avec plusieurs clauses meta_query combinées via AND, chacune ciblant une méta-donnée différente stockée dans la table wp_postmeta.

$produits = new WP_Query( array(
    'post_type'  => 'product',
    'meta_query' => array(
        'relation' => 'AND',
        array(
            'key'     => '_stock_status',
            'value'   => 'instock',
            'compare' => '=',
        ),
        array(
            'key'     => '_sale_price',
            'value'   => '',
            'compare' => '!=',
        ),
        array(
            'key'     => '_price',
            'value'   => array( 10, 50 ),
            'type'    => 'NUMERIC',
            'compare' => 'BETWEEN',
        ),
    ),
) );

Pourquoi c’est un problème

L'essentiel à retenir : postmeta n'est jamais indexé pour un filtrage efficace à grande échelle ; Les tables de lookup natives de WooCommerce couvrent déjà les cas les plus courants ; Une meta_query multiple dégrade la requête de façon non linéaire

La table wp_postmeta stocke toutes les méta-données de tous les types de contenu du site sous forme clé-valeur, avec un index limité principalement sur meta_key et post_id, pas sur la combinaison de valeurs recherchée. Chaque clause meta_query additionnelle se traduit par une jointure supplémentaire sur cette même table, souvent sans pouvoir exploiter un index efficace pour la comparaison numérique ou la clause BETWEEN. Sur un catalogue de quelques centaines de produits, MySQL absorbe cette inefficacité sans qu’elle ne soit perceptible ; sur plusieurs dizaines de milliers de lignes, le temps de requête peut passer de quelques millisecondes à plusieurs centaines, multiplié par chaque visiteur qui charge la page en simultané.

La solution que WooCommerce propose déjà

Pour les critères les plus courants — statut de stock, prix, produits en promotion — WooCommerce maintient depuis plusieurs versions une table de lookup dédiée, wp_wc_product_meta_lookup, spécifiquement indexée pour ces filtres fréquents. Plutôt que de reconstruire une meta_query sur wp_postmeta, il est largement préférable de s’appuyer sur cette table via WC_Product_Query ou une jointure SQL directe quand une requête personnalisée est vraiment nécessaire :

$produits = wc_get_products( array(
    'status'       => 'publish',
    'stock_status' => 'instock',
    'limit'        => 24,
) );

wc_get_products() s’appuie en interne sur les mécanismes optimisés de WooCommerce plutôt que sur une requête meta_query brute, et couvre nativement la plupart des critères de filtrage courants sans qu’il soit nécessaire de réinventer une jointure sur wp_postmeta.

Quand une meta_query reste malgré tout nécessaire

Pour un critère métier propre au projet — par exemple filtrer sur une méta-donnée personnalisée _certification_bio — la table de lookup native ne suffit évidemment pas. Dans ce cas, la bonne pratique consiste à créer sa propre table de lookup dédiée, mise à jour à chaque sauvegarde du produit via le hook woocommerce_update_product, plutôt que de multiplier les clauses meta_query sur wp_postmeta :

add_action( 'woocommerce_update_product', function( $product_id ) {
    global $wpdb;

    $certifie = get_post_meta( $product_id, '_certification_bio', true );

    $wpdb->replace(
        $wpdb->prefix . 'catalogue_certifications',
        array(
            'product_id' => $product_id,
            'bio'        => $certifie ? 1 : 0,
        ),
        array( '%d', '%d' )
    );
} );

Cette table personnalisée, indexée sur la colonne bio, permet ensuite une jointure SQL directe bien plus rapide qu’une meta_query équivalente, tout en restant sous le contrôle total du développeur, sans dépendre d’une structure interne de WooCommerce susceptible d’évoluer.

Ce qui aggrave encore la situation

  • Combiner une meta_query lourde avec un orderby sur une autre méta-donnée non indexée, ce qui ajoute un tri complet en mémoire après la jointure.
  • Répéter cette requête à chaque chargement de page sans aucune forme de mise en cache des résultats, même transitoire.
  • Ignorer l’explication de plan de requête MySQL (EXPLAIN), qui révèle pourtant très rapidement une jointure sans index exploitable.

Un réflexe simple à adopter avant de blâmer le serveur d’hébergement : passer chaque requête suspecte au EXPLAIN de MySQL. Une ligne « Using filesort » ou « Using temporary » sur une requête produit à fort volume signale presque toujours ce type d’antipattern.

Quoi faire concrètement

Avant d’écrire une nouvelle meta_query sur des produits, vérifier systématiquement si WooCommerce ne propose pas déjà un paramètre natif équivalent dans wc_get_products() ou WC_Product_Query. Si le critère est propre au métier du client et absent du cœur de WooCommerce, préférer une table de lookup personnalisée, indexée sur la colonne réellement filtrée, plutôt que d’empiler les clauses sur wp_postmeta. Ce choix, plus coûteux à mettre en place au départ, évite une dégradation progressive et souvent invisible jusqu’à ce que le catalogue atteigne une taille critique.

Partager :

À propos de l'auteur

Clément Hadrot

Développeur WordPress, passionné par Elementor, le FSE et l’automatisation par IA.

Voir tous ses articles

Dans la même veine

À lire aussi