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.

Live output

Enable JavaScript to customise; default output below.

Output

meta_query only gives you the array on its own, for pasting into an existing query.

-1 means every matching post. On a table with a few thousand rows that is a slow page.

Live preview meta-query.php
<?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.

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

  1. Add one clause per meta key you are testing. Each clause is another join on the postmeta table, so three clauses is three joins.
  2. Set the value type. CHAR is the default, which means '10' sorts before '9' until you switch a numeric field to NUMERIC.
  3. Use EXISTS and NOT EXISTS when you only care whether the key is there. They do not take a value.
  4. For IN, NOT IN and BETWEEN, type the values separated by commas. The generated code splits them into the array those operators expect.
  5. Name a clause if you want to order by it. Ordering by a clause name is what replaced the old meta_key plus orderby => meta_value pair.

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_query results 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 EXISTS misses 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.
  • EXISTS and NOT EXISTS ignore value. Passing one anyway does nothing and hides the mistake.
  • relation only 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.
  • LIKE values 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?
The type is CHAR. Set it to NUMERIC so MySQL casts before comparing, or the values sort as text.
Can I mix AND and OR?
Yes, by nesting: a clause can itself be an array of clauses with its own relation. Build the outer one here and nest the inner array by hand.
Is meta_query slow?
Each clause joins postmeta again. One or two clauses on an indexed key is fine; five clauses on a large table is where people start caching the result.
How do I order by a meta value?
Name the clause and pass that name as 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?
Because the key exists with an empty value. NOT EXISTS is about the row, not the content of it.

From the people who built this tool

WP Adminify

The WordPress admin, rebuilt: a dashboard worth looking at, menu and column control, a real file manager and the login page your client sees.

See WP Adminify Free version on WordPress.org

Weekly drops

New tools, when there are new tools

One email when something worth using ships. No schedule to fill, so no filler.

Your address goes nowhere else, and one click unsubscribes.