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 containsJohn
, it can be alsoJohnny
,Johnathan
,somethingJohnsomething
:
$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 toLIKE
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 aboveNOT 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 allREGEXP
(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 toREGEXP
but meta values must not match your given regular expressionRLIKE
(WordPress >= 3.7) – it is the synonym toREGEXP
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
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
Hi man,
This seems like some nice clear examples of the WP_Query. I was working with the WP_Meta_Query and wondered how it was possible to just query for all values under a particular meta key. Say for example you have a custom field called member_city, and many posts use this to give location informations such as “This user is from London”.
What I’m actually trying to do is kind of hard to explain. I’m trying to get all custom meta data from the database, i.e all the cities, then output them into an array which can be used to generate a drop down menu that users can then select from to search users by location. I also don’t want this to duplicate cities, i.e if two people are from London, I don’t want London in the list twice. I’m using WordPress Advanced Search by Growth Spark for my search.
I’m able to query the database and get an output such as:
I just don’t know how to extract that information, then get it into my drop down. Does this make sense to you? May be confusing without code examples and understanding of the site architecture.
Fine and useful examples, happy new year!
Thanks a lot !!!
You are welcome!
Hi, i found this examples very good, but i have a doubt, what if i want to order by a metabox value. For example, I have a custom post type named team_member and this post-type have a metabox named order, later in the loop, i need query the post but ordering by this order that i defined in the metabox, How i could do this?
Thanks in advance.
Please sorry my english….
Hi,
WP_Query supports the
orderby
parameter, so it should look like this:thanks Misha, I try your code, but instead ‘orderby’=>’meta_value’ I used ‘orderby’=>’meta_value_num’ and it’s working perfect!!!! Thanks a lot for the answer.
Regards
Juankql
I just want to thank you for all the examples you’ve posted. It has given me some way to be able to start off with the project I’m doing, based on a query on premium post listing. Again, thank you.
I have a key that has 2 meta options: “id”:”12345″ , and “select new”
How can i return only the first? (the ome with the id?
Let me clarify — you have two meta values with the same key and you want to get posts with the first value only, is that right?
thanks @Misha for your reply :)
yes, the key is the same and i want to get in return only {“id”:”12345″} << which comes like this (including the ' " ' charachter and the ' : '.)
thanks !
I hope it helps :)
sure did!
thank you very much :)
Hi Misha, thanks for this info…. and since you seem to be very proficient in Wp_Query… Can I pick your brain a little bit and ask for your help:
I need to display posts (tours) that are in a specific location (city) so in my location_taxonomy.php I have a meta query in the form of:
When the key stored in the tour has only one value (one city), it displays the posts. When the key has more than one value (multiple cities or a string of ids separated by commas), it doesn’t.
How can I change the $args so the query would determine that the current city ($term->term_id) exists within the comma separated values stored as a string in ‘trav_tour_city’?
I have tried something in the line of:
and also I tried “LIKE”, but obviously I don’t know what I’m doing… haha..
any help would be greatly appreciated!
Regards,
Ivan
Hi Ivan,
LIKE should help:
This did the trick:
I will try yours also…
Thanks a lot for your input!
i have a custom -post-type that has 2 custom filed checkboxes
and i need to print only one cpt value for each selected field
and for each field i wan to have different ouptput
howcan i do that?
I’m not sure that I understand you, but maybe this post is what you are looking for.
Hi, Misha How are you? Just have little question about meta query. My meta query working fine in single data, but it can’t working if i use array in there. Any other need to do? i have follow all your step. Thanks :)
Hi Diaz,
Can I look at your meta query? 🙃
Thank you! Really helped me!
Hi Misha,
This is a great article!
I have a situation where a user enters a search terms and that populates the value in the argument via a variable. However, the field expects something to be entered before any results show.
So, do you know if it is possible to set the argument to show all results that can be later filtered to a smaller list of results?
I raised a ticket about it here that shows the code:
https://stackoverflow.com/questions/48725474/show-all-wordpress-authors-then-filter-the-list-by-meta-key
Thanks for your time
Damien
Hi Misha,
No need to worry about this, I actually had a misplaced IF statement in my code that caused this to not work. All good now.
Thanks
Hi Damien,
I’m glad you figured it out 🙂
For me, this didn’t work until I replaced “key” with “meta_key” and “value” with “meta_value”. ie:
I suggest looking into your phpAdmin page and opening the “metadata” table “structure” tab to make sure.
Very strange,
meta_key
andmeta_value
params are OK when using them outside meta_query.Hi,
Thank you for this post.
I am trying to put up a meta query to search for values that are in serialised format, for example:
a:3:{i:0;s:28:”Primary School (Classes 1-5)”;i:1;s:27:”Middle School (Classes 6-8)”;i:2;s:31:”Secondary School (Classes 9-10)”;}
My code is below:
But it does not give me desired results. Any help is appreciated.
Thanks!
Hello,
I have a problem with the sorting of my products. I have products which have no price and I want them to be in the end after all products with price. Can you please help me? Thanks!
Hello,
thank you for the post.
I wanted to get all the posts if the user selects no meta query. I tried this but it does not work.
This return no posts, I wanted if the value is empty, all posts should be shown.
Can you Please help me?
Hello,
Did you try
NOT EXISTS
? Example:Hi!
I looking for solution for my problem and find your post.
The intention is to search for customers who do not have coupon X or do not have any coupon.
I am doing according to other answers I found on the web, but the query returns wrong when it finds a post that has a meta_value different from the one searched.
Great article full of really helpful examples. Helped me out a lot today – thanks!
Hey, thanks for this!
what about this goal: to get all the posts (and cpts) that have a meta_key ‘my_meta_name’ corresponding to the post’s title, and to order them by their other meta_key ‘my_meta_date’, in DESC order.
I’ve tried this:
But no luck so far. The above stuff returns a bunch of posts not corresponding to my needs and not even correctly ordered :-)
Could you please enlighten me?
P.S. WP version is 5.3.2
Hey Marco,
Well, you code is far away from perfection, sorry 🙃
Hey Misha,
thanks for replying, hehe yeah I kinda thought it was a mess :-)
I also came out after a couple of hours with almost the exact code you’ve posted, but I still get mixed results so I guess the catch is somewhere else.
Thanks again!
Thanks for the nice write up! Helped me a lot today.
Hello again Misha, I hope you are well!
Probably I already have the answer (which would be “I can’t”), but I want to check it with you, if you will.
Basically I need to query either:
a) for posts with the meta_key “my_color” and meta_value “red”
…or…
b) for posts with the meta_key “my_color” and meta_value “yellow”, AND post_content not empty.
So it’s a mixture of a meta_query and a “normal” query clause… and I think I cannot do it with just WP_Query, is that correct?
P.S. The solution I’ve found so far is to simply add a postmeta that tells wether the post_content is empty or not, auto-updating itself on save_post. In this way I am able to query the posts I wanted with the sole use of meta_query. I am a bit concerned about performance (I have thousands of posts), but I don’t know if there is any other viable solution.
Cheers!