Using meta_query to Query Posts by Postmeta

A frequently overlooked enhancement that shipped with WordPress 3.1 has made querying posts by postmeta values significantly simpler. The WordPress core team made this improvement by adding the “meta_query” parameter to the WP_Query class. Previously, you could query and filter posts using a combination of the “meta_key”, “meta_value”, and “meta_compare” parameters; however, this came with a major limitation–you could only filter by a single postmeta field. With WordPress 3.1 and the addition of the “meta_query” parameter, you can query for posts based on numerous postmeta values.

To illustrate the power of this enhancement, imagine that you have a custom post type called “product” that allows you to manage your T-shirt inventory. Upon saving each product, you enter custom field values for “price”, “size”, and “sex”. Recognizing that you have an overstock of size “S” shirts for “men” that are under “$15.00″, you want to run a marketing campaign on your website to highlight shirts that fit this criteria. As such, you are faced with the task of creating a page that only displays these items. In order to query for these products, you can use “meta_query” in the following manner.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$args = array(
	'post_type' => 'product',
	'meta_query' => array(
		array(
			'key' => 'price',
			'value' => '15.00',
			'compare' => '<',
			'type' => 'NUMERIC'
		),
		array(
			'key' => 'size',
			'value' => 'S',
			'compare' => '=',
			'type' => 'CHAR'
		),
		array(
			'key' => 'sex',
			'value' => 'men',
			'compare' => '=',
			'type' => 'NUMERIC'
		)
	)
 );
$query = new WP_Query( $args );

In this example, for each key that you want to search by, you need to create a new array with parameters for the search. The first criteria for our query is that the price is less than $15. As such, I created an array that specified the key to be ‘price’, the criterion value to be ’15.00′, and the comparison operator to be ‘<'. Additionally, I added the type parameter and specified it to be 'NUMERIC' to inform the search that it is comparing numeric values. This array tells WP_Query that my first criteria is a post with an associated price less than $5. The remaining two criteria are specified in the same manner. As a result, I will only grab those posts that needed for the promotion. The results can then be printed out using the WordPress loop as usual.

Now, imagine that you want to order these posts by price, from lowest to highest. While one might think that simply specifying "orderby=price" would complete the task, it is not quite that easy. Rather strangely, to order your posts by postmeta values, you still need to specify the "meta_key" parameter in your arguments. Having to do this is confusing as this parameter is marked as "deprecated" in the WordPress Codex. This ordering would be accomplished with the following code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
$args = array(
	'post_type' => 'product',
	'meta_query' => array(
		array(
			'key' => 'price',
			'value' => '15.00',
			'compare' => '<',
			'type' => 'NUMERIC'
		),
		array(
			'key' => 'size',
			'value' => 'S',
			'compare' => '=',
			'type' => 'CHAR'
		),
		array(
			'key' => 'sex',
			'value' => 'men',
			'compare' => '=',
			'type' => 'NUMERIC'
		)
	),
	'meta_key' => 'price',
	'orderby' => 'meta_value',
	'order' => 'ASC'
 );
$query = new WP_Query( $args );

Specifying the ‘meta_key’ parameter tells the function which field should be used for ordering the posts according to meta_values.

Prior to WordPress 3.1, this task was much more difficult. One would have to write a custom query or filter results after retrieving them from the database. While both strategies can be made to work, they are significantly more complicated and are not as future proof as using “meta_query”.

In summary, the addition of the “meta_query” parameter to the WP_Query class has made querying for posts based on meta_values a much simpler task and has introduced the ability to query by multiple meta_values.

This article was authored by:

I am a Senior Web Engineer at 10up LLC. While I am well versed in general web development, WordPress is my bread and butter. I enjoy working on WordPress projects, writing plugins, and helping other learn about appreciate how incredible the WordPress software is. After about 8 years doing web development, I have finally launched my own website. I’d like to think it’s because I finally feel that I have something to say, but really, it’s actually because I was finally able to design a theme that I did not loathe.

Zack Tollman has authored 6 posts.Visit Website

Showing 27 Comments

  • Can that technique be used to generate a WP_Query using form fields?

    In other words, on the front end, you get a form, with 3 fields:

    1- price (a text box)
    2- size (a drop down menu with S, M, L, XL…etc.)
    3- sex ( a radio button with 2 values, Male, Female)

    Once the user makes the selections, the appropriate WP_Query is executed.

    Is that possible? and how?:)

    REPLY
  • Zack Tollman (Admin)

    @Ayman

    Absolutely! I would follow the following steps to make something like this work:

    1) Create the form
    2) On submission, have the script notice that the form has been submitted and modify the variables based on the input
    3) Use that input to send it to the query

    REPLY
  • I have not been able to get this to work in 3.1.4.

    $paged = (get_query_var(‘paged’)) ? get_query_var(‘paged’) : 1;
    $args=array(
    ‘post_type’ => ‘jsw_coach’,
    ‘meta_query’ => array(
    array(
    ‘key’ => ‘_lastNameFirst’,
    ‘value’ => $lastnamefirst
    ),
    array(
    ‘key’ => ‘_coachSport’,
    ‘value’ => $coachsports,
    ‘compare’ => ‘=’
    ),
    array(
    ‘key’ => ‘_coachCity’,
    ‘value’ => $coachcities,
    ‘compare’ => ‘=’
    )
    ),
    ‘meta_key’ => ‘_lastNameFirst’,
    ‘orderby’ => ‘meta_value’,
    ‘order’ => ‘ASC’,
    ‘paged’=>$paged
    );

    query_posts($args);

    The loop works; my posts are filtered correctly by the meta_query; but it just doesn’t sort. Do you see something I’m missing? Thanks.

    REPLY
  • Zack Tollman (Admin)

    John…I’m stumped by this. I would pull out the ‘paged’ parameter just to see what happens. I don’t expect this to do much, but I’d like to rule it out as the culprit.

    The other issue could be your use of ‘query_posts’. If you are not careful with ‘query_posts’ you can get very unexpected results with your queries depending on the context. There is a great explanation of this here: http://wordpress.stackexchange.com/questions/1753/when-should-you-use-wp-query-vs-query-posts-vs-get-posts

    As an alternative, I’d recommend using

    $query = new WP_Query( $args );

    Then modify the loop control like so:

    have_posts()) : ?>
    have_posts()) : $query->the_post(); global $post; ?>

    REPLY
  • Zack Tollman (Admin)

    Oops…my php gotten mangled! Here’s what it should look like: http://pastebin.com/55XXh3Hd

    REPLY
  • Sue

    Thanks for the information Zack, it’s really helpful. Do you think it’s possible to use meta query to check a custom field contains a string…

    e.g. My custom field ‘country’ = USA, Australia, Spain
    If a user specifies that they want to see American and Australian posts… but not Spanish, can I do that?

    I’ve tried this array

    array(
    ‘key’ => ‘country’,
    ‘value’ => ‘(USA, Australia)’,
    ‘compare’ => ‘IN’
    );

    Do you think that makes sense?

    REPLY
  • Sue

    Re-reading and that may not make sense! Basically every post can have one of the following Strings in the custom field called ‘country’

    1. USA, Australia, Spain
    2. USA, Australia
    3. USA, Spain
    4. USA
    5. Australia, Spain
    6. Australia
    7. Spain

    If the user decides they want to see posts available in USA or Australia I want to show every option except no. 7.

    REPLY
  • Zack Tollman (Admin)

    Sue,

    Great question! You absolutely can match a string. Actually, by default, the “compare” parameter is “=”. I’ve posted a pastebin of what should work for you: http://pastebin.com/qH3k7u3P.

    I would encourage you to think about using a taxonomy (e.g., “Category”) to manage this data. It sounds like what you are doing is organizing posts by this country data, which is exactly what taxonomies are meant to do. In this case you might want to create a custom taxonomy (http://codex.wordpress.org/Function_Reference/register_taxonomy) called “Country”. The advantage of doing so is that you can utilize all of WP’s built in functions for handling taxonomies.

    Let me know if this works out for you!

    REPLY
  • Sue

    Thanks so much Zack. Unfortunately it doesn’t seem to return any results for me.

    If I go down the custom taxonomy route, is it possible to query posts on more than one custom taxonomy at once?

    I was wondering in which circumstances its better to add a taxonomy to a custom post type, and which circumstances its better to use custom fields. I haven’t seemed to come across a definitive answer!

    Thanks again :)

    REPLY
  • Zack Tollman (Admin)

    Sue,

    Can you show me the whole $args variable that you are using? There could be a number of reasons why you are getting no results.

    With taxonomies, you can definitely query posts based on numerous taxonomies. In fact, there is a “tax_query” parameter that does just that. It works similarly to the “meta_query” parameter. Take a look here: http://codex.wordpress.org/Class_Reference/WP_Query#Taxonomy_Parameters.

    Generally speaking, I turn to taxonomies when 1) I will be using the data to organize the post types, or 2) I expect a lot of repeated values. For instance, if you are entering the country variables over and over again, it can be exhausting and if you make a mistake in your entries, you have to go through each one to make the correction. Countries is going to be repeated throughout your project. As such, I think it would make sense to enter your possible countries in the taxonomy page and simply check them for your posts. You’ll also want to use taxonomies when you have hierarchical data.

    Now, if you wanted to include individual addresses for your posts (e.g., 123 S Main St.), that would be more of a post meta situation. These values would likely all be unique so it is unnecessary to have a taxonomy for them.

    Hopefully this helps!

    REPLY
  • Sue

    That makes lots of sense with the taxonomies! Thank you.

    This is my code to try find all custom posts of type ‘wdl’, that have ‘vacations’ ticked in the wdl_supplier_type taxonomy and that have 2 custom fields ‘country’ containing USA, Australia or both, and also offer sun or ski vacations or both.

    http://pastebin.com/D1tVYKpx

    REPLY
  • Zack Tollman (Admin)

    Sue,

    Give this a whirl: http://pastebin.com/RNyyKJ8R. You cannot query for taxonomy terms in the manner that you used. If this doesn’t work, I’d recommend getting rid of the taxonomy business and see if the meta_query is working. If it is, then you at least know that it is working and can move on to focus on the taxonomy issue.

    Also, if you pastebin again, include the code after you set the $args variable. I’d be interested in seeing what you are doing there.

    Good luck!

    REPLY
  • Sue

    Perhaps the problem is that when a custom post should be marked for two countries I have one custom meta field called ‘country’ and it contains ‘Australia, USA’ rather than having two separate ‘country’ meta fields for the post, with one containing Australia and one containing USA.

    REPLY
  • Zack Tollman (Admin)

    Sue,

    Absolutely! I thought you were entering something like “(USA,Australia)” into the custom field. If you want it to match multiple values, you would use “IN” as the “compare” parameter, but then, you need to set your “value” parameter to an array of values:

    “value” => array(
    ‘USA’,
    ‘Austria”
    );

    REPLY
  • Sue

    Thanks Zack.

    The weird thing is.. the taxonomy part was working ok..
    ‘wdl_supplier_type’ => ‘wedding_photographers’

    until I added the meta_query. The meta query works fine too alongside the taxonomy, once I only look for one country in ‘country’.
    e.g. ‘value’ => ‘USA’,

    The problem / zero results arises when I try look for ‘country’ to contain one or more countries from a list.
    e.g ‘value’=>’(USA,Australia)’

    REPLY
  • Zack Tollman (Admin)

    See my comment below.

    REPLY
  • Zack Tollman (Admin)

    Here’s what I mean:

    http://pastebin.com/uc6Jvcue

    REPLY
  • Sue

    Thanks Zack. This is what I have: http://pastebin.com/ZKk8F0ft

    For some reason your custom taxonomy method isn’t working for me, but the old method is fine.

    Also adding meta_data into the mix produces in zero results.

    REPLY
  • Sue

    I checked the wp_postmeta table, and the meta data is being stored as meta_key = _ct_multiselectbox_4e46766e7ada
    meta_value = a:2:{i:1;s:9:”Reportage”;i:2;s:11:”Traditional”;}

    REPLY
  • Zack Tollman (Admin)

    Sue…please see my email. We are overwhelming the threaded comment system to the point that I cannot respond to your latest questions. Let’s work out a solution via email then post back here the results.

    REPLY
  • Van

    I know this is an old post, but I thought someone may be able to tell me why these args to get_posts aren’t sorting by this custom post type correctly. They should sort on the custom meta field ‘distributor_monthly_sales’ NUMERICALLY, but instead they are sorting like ASCII, so that 800 is greater than 7000:

    $args = array(
    ‘post_type’ => ‘distributor’,
    ‘numberposts’ => $count,
    ‘post_status’ => ‘publish’,
    ‘meta_query’ => array(
    array(
    ‘key’ => ‘distributor_primary_category’,
    ‘value’ => $category,
    ‘compare’ => ‘=’,
    ‘type’ => ‘CHAR’
    ),
    array(
    ‘key’ => ‘distributor_monthly_sales’,
    ‘value’ => 0,
    ‘compare’ => ‘>’,
    ‘type’ => ‘NUMERIC’
    ),
    ),
    ‘meta_key’ => ‘distributor_monthly_sales’,
    ‘orderby’ => ‘meta_value’,
    ‘order’ => ‘DESC’,
    );

    Itt’s pulling the correct posts, but I just can’t seem to get the ORDER BY to sort numerically. (I double-checked that I’m using intval() when I’m storing the values, although WP may cast everything to strings when it updates the postmeta table.)

    Shouldn’t ‘type’ => ‘NUMERIC’ stick for the orderby clause?

    REPLY
  • Chris Ellison (Admin)

    Van, try setting your “orderby” parameter to “meta_value_num” (instead of “meta_value”) so that WordPress knows to expect to sort numerically.

    REPLY
  • Where am I supposed to place the code given?

    Must I use a custom post type (product in this case)? Isn’t it possible just to sort the posts listed in index.php based on their meta value?

    REPLY
  • Thanks Zack!

    I have a more complicated scenario – let’s say you want to order the t-shirt name by it’s string length…

    I have a CPT ‘employees’ and a custom field ‘first-name’, and I’d like to order theme by the ‘first-name’ string length, any ideas how can i do that ?

    Thanks,
    Amit

    REPLY
  • I’m trying to search the products based on custom attributes(country, days, budget) that I’ve created. When I submit, the form disappears and nothing is displayed. Where am I going wrong? Kindly note that I’m very new to wordpress and woocommerce. I read a couple of sites and came up with this. Currently there is only one product for which I’ve set the custom attributes. So the code should display only that product.

    This is the code for my search form.
    http://pastebin.com/RriCEtNb

    On submitting this form the control is transferred to submit.php which has the following code:

    http://pastebin.com/7Wf9uFbf

    REPLY
  • Hi,
    i would like to generate a WP_Query using 3 form fields?

    In other words, on the front end, i would liek to have a form which has the following inputs –
    1 – custom search text box (to search for CPT – products)
    2- price (Low to High)
    3- price (High to Low)

    Once the user makes the selections, the appropriate WP_Query is executed.

    Is it possible and how? Thanks in advance.

    REPLY
  • I am new to WordPress and I want to sort products in woo-commerce to show first all featured products and then the rest of the products from high to low price in catalog mode.
    is it possible?

    REPLY

Add Your Voice: