Meta_Query of WP_Query

In this guide I am assuming that you already have some basic knowledge how to work with WP_Query class in WordPress. And this tutorial will be all about working with custom fields.

Either you are working with Gutenberg or with Classic Editor, maybe you’re using some custom fields plugins like ACF, Carbon Fields, Simple Fields (it is my plugin, highly recommend) – in all of these cases the idea of working with Meta_Query will be the same.

Not meta_query yet – using meta_key and meta_value

Before we dive into actual Meta_Query examples, I definitely should show you how to work with meta_key and meta_value arguments of WP_Query, we usually used them before meta_query appeared but sometimes using them is really helpful.

Let’s assume that you need to get all posts with the meta key show_on_homepage and meta value yes. You can do it that way:

$args = array(
	'meta_key' => 'show_on_homepage',
	'meta_value' => 'yes',
);
 
$q = new WP_Query( $args );

Otherwise, if you need to query all posts except the ones with this pair of meta key and value, you can use the following parameters:

$args = array(
	'meta_key' => 'show_on_homepage',
	'meta_value' => 'yes',
	'meta_compare' => '!=',
);
 
$q = new WP_Query( $args );

As you can see I used another parameter meta_compare that allows to test meta_value. Its possible values are = (default), !=, >, >=, <, <=, LIKE, NOT LIKE, IN, NOT IN, BETWEEN, NOT BETWEEN, NOT EXISTS, REGEXP, NOT REGEXP or RLIKE. Please do not worry, we will discuss most of them later in this tutorial.

Query Posts by Meta Keys and Meta Values

Get posts by a single values of meta key and meta value

This is exactly the example we discussed before but using meta_key and meta_value arguments. Not it is time to do the same with meta_query.

// the meta_key 'color' with the meta_value 'white'
$args = array(
	'meta_query' => array(
		array(
			'key' => 'color',
			'value' => 'white',
		)
	)
);
 
$q = new WP_Query( $args );

Query posts by multiple meta values

Now let’s get all the posts with white OR green “color” custom field value. If you need to provide a list of values to compare meta value agains, you can specify any amount of them as an array and also add compare parameter IN.

// custom field name is color and custom field value is 'white' OR 'green'
$args = array(
	'meta_query' => array(
		array(
			'key' => 'color',
			'value' => array( 'white', 'green' ),
			'compare' => 'IN',
		)
	)
);
 
$q = new WP_Query( $args );

Query posts with meta values not equal to the given one

Let’s do the opposite thing – get all the posts except the ones with meta key “color” and meta value white. This time parameter compare comes into play again.

$args = array(
	'meta_query' => array(
		array(
			'key' => 'color',
			'value' => 'white',
			'compare' => '!=',
		)
	)
);
 
$q = new WP_Query( $args );

For multiple values to compare please use compare parameter NOT IN. Let’s all the posts (products in online shop for example) except white products and green products:

$args = array(
	'meta_query' => array(
		array(
			'key' => 'color',
			'value' => array( 'white', 'green' ),
			'compare' => 'NOT IN',
		)
	)
);
 
$q = new WP_Query( $args );

Query posts by multiple meta keys

Let’s combine the two example we discussed about – the example about showing the post on the homepage and the example of querying posts by “color” meta key. We can easily do that with relation parameter.

$args = array(
	'meta_query' => array(
		'relation' => 'AND', // "OR" or "AND" (default)
		array(
			'key' => 'show_on_homepage',
			'value' => 'yes',
		),
		array(
			'key' => 'color',
			'value' => array( 'white', 'green' ),
			'compare' => 'IN',
		)
	)
);
 
$q = new WP_Query( $args );

So, both of the conditions should be applied. If just one of the condition should match, you can use 'relation' => 'OR', in the example above it can even be skipped because we are using its default value.

Complex conditions

In the above example we used two conditions in meta_query, but there could be many more of them and on a different levels. For example:

$args = array(
	'meta_query' => array(
		'relation' => 'AND',
		array(
			'key' => 'key_1',
			'value' => 'value_1',
		),
		array(
			'relation' => 'OR',
			array(
				'key' => 'key_2',
				'value' => 'value_2'
			),
			array(
				'key' => 'key_3',
				'value' => 'value_3',
			),
		),
	),
);
$q = new WP_Query( $args );

If we could translate it to a regular PHP condition we will write it this way:

if( 'value_1' == $key_1 && ( 'value_2' == $key_2 || 'value_3' == $key_3 ) ) {

I hope it is clear how it works and isn’t meta_query powerful?

Compare Parameter of Meta_Query

I promised you above that will definitely dive deeper into how Meta_Query compare parameter can be used. Let’s do it now.

  •  (or not set) Equal to,
  • != – Not equal to,
  • < – Less than,
  • <= – Less or equal to,
  • > – Greater than,
  • >= – Greater than or equal to, example:
// the product price in this example is 2000 or more than 2000:
$args = array(
	'meta_query' => array(
		array(
			'key' => 'price',
			'value' => 2000,
			'type' => 'numeric', // specify it for numeric values
			'compare' => '>='
		)
	)
);
  • LIKE – Allows to search in meta values for a specific string, in the example below the query returns all the posts where “first_name” name contains John, it can be also JohnnyJohnathansomethingJohnsomething:
$args = array(
	'meta_query' => array(
		array(
			'key' => 'first_name',
			'value' => 'John',
			'compare' => 'LIKE'
		)
	)
);
  • Two more things: first – it is not case sensitive, second – wildcard symbols @ are not necessary.
  • NOT LIKE – similar to LIKE just works in an opposite way – meta value mustn’t contain the given string.
  • IN – posts meta value must contain one of the values of the given array, you can see the example above
  • NOT IN – meta values must not contain ANY of the values in the given array.
  • BETWEEN – post meta value should be between the given range of values, example:
// the product price is more than 2000 and less than 4000
$args = array(
	'meta_query' => array(
		array(
			'key' => 'price',
			'value' => array( 2000, 4000 ),
			'type' => 'numeric',
			'compare' => 'BETWEEN'
		)
	)
);
  • It can also work for dates, check the example below too.
  • NOT BETWEEN – not in a given range.
  • EXISTS (WordPress >= 3.5) – If meta value of a specific meta key exists or empty / null.
$args = array(
	'meta_query' => array(
		array(
			'key' => 'misha_key',
			'compare' => 'EXISTS'
		)
	)
);
  • So, actually it checks if a meta key exists, you do not even have to pass any value with this parameter.
  • NOT EXISTS (WordPress >= 3.5) – if a given meta key doesn’t exist at all
  • REGEXP (WordPress >= 3.7) – it allows you to compare meta values with regular expression, example:
$args = array(
	'meta_query' => array(
		array(
			'key' => 'misha_key',
			'value' => '^[0-9]*$', // "misha_key" must be only numbers
			'compare' => 'REGEXP'
		)
	)
);
  • NOT REGEXP (WordPress >= 3.7) – Similar to REGEXP but meta values must not match your given regular expression
  • RLIKE (WordPress >= 3.7) – it is the synonym to REGEXP

Using compare for date values

You can also use meta_query to check if a custom field is between two dates.

The most important thing you have to keep in mind is that your date format should be like YYYY-MM-DD or YYYY/MM/DD or something like that (year goes first, then month, then day, then time if you need it too). Check how it is stored in database – if it is stored in another format, for example days go first, then filtering won’t work for you until you change dates presence in database.

array(
	'key' => 'sale_day',
	'value' => array( '2018-01-01', '2018-01-09' ),
	'compare' => 'BETWEEN',
)

If the date is stored in UNIX time, example: 1543391233, everything became super simple:

array(
	'key' => 'sale_day',
	'value' => array( strtotime( '2018-01-01' ), strtotime( '2018-01-09' ) ),
	'type' => 'numeric',
	'compare' => 'BETWEEN',
)

Ordering Posts by Meta Values

I will show you different examples of how you can order your posts by meta values. First of all, look at this simple code sample, where we don’t even use meta query:

$args = array(
	'meta_key' => 'misha_key',
	'orderby' => 'meta_value'
);

Now, let’s say that misha_key contains only numeric values, we can change our code for that case just a little bit.

$args = array(
	'meta_key' => 'misha_key',
	'orderby' => 'meta_value_num'
);

But how to use it together with meta query? Simple, just change our code the following way:

$args = array(
	'meta_query' => array(
		'misha_clause' => array(
			'key' => 'misha_key',
			'compare' => 'EXIST'
		)
	),
	'orderby' => 'misha_clause'
);

Order by multiple meta keys

$args = array(
	'meta_query' => array(
		'price_clause' => array(
			'key' => 'price',
			'value' => array( 2000, 4000 ),
			'type' => 'numeric',
			'compare' => 'BETWEEN',
		),
		'misha_clause' => array(
			'key' => 'misha_key',
			'compare' => 'EXISTS',
		), 
	),
	'orderby' => array(
		'price_clause' => 'ASC',
		'misha_clause' => 'DESC',
	),
);

So, first of all we order posts by price_clause ascending and then we order posts by misha_clause descending.

Misha Rudrastyh

Misha Rudrastyh

Hey guys and welcome to my website. For more than 10 years I've been doing my best to share with you some superb WordPress guides and tips for free.

Need some developer help? Contact me

Follow me on X