WP Meta Query Generator
Build a WordPress meta_query array: clauses, comparison operators, value types and the relation between them, ready to drop into WP_Query.
<?php
/**
* A meta_query built from post custom fields.
*/
$args = array(
'post_type' => 'post',
'posts_per_page' => 10,
// Meta queries cannot use the object cache, so keep the result set small.
'meta_query' => array(
array(
'key' => 'featured',
'value' => '1',
'compare' => '=',
'type' => 'CHAR',
),
),
);
$query = new WP_Query( $args );
if ( $query->have_posts() ) {
while ( $query->have_posts() ) {
$query->the_post();
the_title( '<h2>', '</h2>' );
}
// Always restore the global post after a custom loop.
wp_reset_postdata();
}
Output is valid and updates as you type.
Fix the highlighted fields to update the output.
Pick the meta keys, the comparison and the value type, and the generator writes the meta_query array with the relation, the named clauses and the ordering that goes with them.
How to use
- Add one clause per meta key you are testing. Each clause is another join on the postmeta table, so three clauses is three joins.
- Set the value type.
CHARis the default, which means'10'sorts before'9'until you switch a numeric field toNUMERIC. - Use
EXISTSandNOT EXISTSwhen you only care whether the key is there. They do not take a value. - For
IN,NOT INandBETWEEN, type the values separated by commas. The generated code splits them into the array those operators expect. - Name a clause if you want to order by it. Ordering by a clause name is what replaced the old
meta_keyplusorderby => meta_valuepair.
Example
Posts priced between 10 and 50, ordered by that price, with the clause named so the ordering can find it:
$args = array(
'post_type' => 'product',
'posts_per_page' => 12,
'meta_query' => array(
'price_clause' => array(
'key' => '_price',
'value' => array_map( 'trim', explode( ',', '10, 50' ) ),
'compare' => 'BETWEEN',
'type' => 'NUMERIC',
),
),
'orderby' => 'price_clause',
'order' => 'ASC',
);
Without 'type' => 'NUMERIC' that range is compared as text, and 100 falls outside a range of 10 to 50.
Pitfalls
meta_queryresults are not cached the way a plain post query is. A query with several clauses on a large site is a real cost on every page load.- The default type is
CHAR. String comparison puts 100 before 9, which is usually reported as “ordering is broken”. NOT EXISTSmisses posts where the key exists with an empty value. Delete the meta rather than storing an empty string if you want the two to mean the same thing.EXISTSandNOT EXISTSignorevalue. Passing one anyway does nothing and hides the mistake.relationonly applies with two or more clauses. On a single clause it is ignored, which makes a stray'relation' => 'OR'look harmless until a second clause appears.- Ordering by a clause needs the clause to be named and the name to match exactly. A typo gives you the default ordering with no warning.
LIKEvalues are wrapped in%by WordPress. Adding your own gives you%%term%%, which matches nothing.- Serialized values cannot be queried reliably. Store one row per value instead of one row holding an array.
Compatibility
meta_query has been in WP_Query since WordPress 3.1, nested clauses since 4.1, and ordering by a named clause since 4.2. BETWEEN with a comma separated list needs an array, which is what the generated code builds. The generated code targets PHP 7.0 and up, and the tool runs entirely in your browser.
Frequently asked questions
Why is my numeric comparison wrong?
CHAR. Set it to NUMERIC so MySQL casts before comparing, or the values sort as text.Can I mix AND and OR?
relation. Build the outer one here and nest the inner array by hand.Is meta_query slow?
How do I order by a meta value?
orderby. It is clearer than the older meta_key plus orderby => meta_value pairing and works with several clauses.Why does NOT EXISTS miss posts?
NOT EXISTS is about the row, not the content of it.