One of the new things in 3.1 that hasn’t got a lot of discussion yet is the new Advanced Taxonomy Queries mechanism. At the moment, this is still being actively developed, but the basic structure is finalized enough to give at least a semi-coherent explanation on how to use it. Since 3.1 is now going into beta, it’s unlikely to change much.

What’s a Query?

In WordPress land, a “query” is anything that gets Posts. There’s a lot of ways to do this.

  • You can use query_posts to modify the main query of the page being displayed.
  • You can create a new WP_Query, to get some set of posts to display in its own custom Loop.
  • You can do a get_posts to get some limited set of posts for display in some special way.

Regardless of the method, you have to pass parameters to it in order to specify which posts you want. If you’ve used this at all before, then you’ve used parameters like cat=123, or tag=example, or category_name=bob and so forth. When custom taxonomies were developed, you were eventually able to specify things like taxonomy=foo and term=bar and so on.

Querying for Posts

The problem with these is that people sometimes want to specify more than one of these parameters, and not all parameters worked well together. Things like cat=a and tag=b, or cat=a and tag is not b, and so forth. This is because cat and tag are both forms of taxonomies, and the code didn’t handle that well. Sure, some of it worked, for specific cases, but those were mostly there by accident rather than by design. In other words, those cases worked because the system just happened to get it right for that particular instance.

Well, all these old methods still work, but they have been made into a more comprehensive system of generically specifying arbitrary taxonomies to match against. When you specify cat=123, it’ll actually be converting it to this new method internally.

Query Strings are for Suckers

One side effect of this new system is that it doesn’t really work with query strings very well. It can be done, but it’s a lot easier and more sensible if you just start getting into the array method of doing things instead. What’s the array method? I’ll explain:

Imagine you used to have a query that looked like this:

query_posts('cat=123&author=456');

A simple query, really. The problem with it is that WordPress has to parse that query before it can use it. But there is another way to write that query as well:

query_posts(array(
  'cat' => 123,
  'author' => 456,
) );

Essentially, you separate out each individual item into its own element in an array. This actually saves you some time in the query because it doesn’t have to parse it (there’s very little savings though).

The advantage of this is that you can build your arrays using any method of array handling you like. Here’s another way to do it:

$myquery['cat'] = 123;
$myquery['author'] = 456;
query_posts($myquery);

Simple, no? But what if you have to deal with the $query_string? The $query_string is that old variable that is built by WordPress. It comes from the “default” query for whatever page you happen to be on. One of the main uses for it was to deal with “paging”. A common method of doing it was like this:

query_posts($query_string . '&cat=123&author=456');

If you use arrays, you have to deal with that yourself a bit. There’s a couple of possible ways to do it. The easiest is probably to just parse the query string yourself, then modify the result as you see fit. For example:

$myquery = wp_parse_args($query_string);
$myquery['cat'] = 123;
$myquery['author'] = 456;
query_posts($myquery);

I started out with the $query_string, used wp_parse_args to turn it into an array, then overwrote the bits I wanted to change and performed the query. This is a handy technique I’m sure a lot of people will end up using.

On to Advanced Taxonomies

Advanced Taxonomy queries use a new parameter to the query functions called “tax_query”. The tax_query is an array of arrays, with each array describing what you want it to match on.

Let’s lead by example. We want to get everything in the category of “foo” AND a tag of “bar”. Here’s our query:

$myquery['tax_query'] = array(
	array(
		'taxonomy' => 'category',
		'terms' => array('foo'),
		'field' => 'slug',
	),
	array(
		'taxonomy' => 'post_tag',
		'terms' => array('bar'),
		'field' => 'slug',
	),
);
query_posts($myquery);

Here we’ve specified two arrays, each of which describes the taxonomy and terms we want to match it against. It’ll match against both of them, and only return the results where both are true.

There’s two things of note here:

First is that the “field” is the particular field we want to match. In this case, we have the slugs we want, so we used “slug”. You could also use “term_id” if you had the ID numbers of the terms you wanted.

Second is that the “terms” is an array in itself. It doesn’t actually have to be, for this case, as we only have one term in each, but I did it this way to illustrate that we can match against multiple terms for each taxonomy. If I used array(‘bar1′,’bar2’) for the post_tag taxonomy, then I’d get anything with a category of foo AND a tag of bar1 OR bar2.

And that second item illustrates an important point as well. The matches here are actually done using the “IN” operator. So the result is always equivalent to an “include” when using multiple terms in a single taxonomy. We can actually change that to an “exclude”, however, using the “operator” parameter:

$myquery['tax_query'] = array(
	array(
		'taxonomy' => 'category',
		'terms' => array('foo', 'bar'),
		'field' => 'slug',
		'operator' => 'NOT IN',
	),
);
query_posts($myquery);

The above query will get any post that is NOT in either the “foo” or “bar” category.

But what about terms across multiple taxonomies? So far we’ve only seen those being AND’d together. Well, the “relation” parameter takes care of that:

$myquery['tax_query'] = array(
	'relation' => 'OR',
	array(
		'taxonomy' => 'category',
		'terms' => array('foo'),
		'field' => 'slug',
	),
	array(
		'taxonomy' => 'post_tag',
		'terms' => array('bar'),
		'field' => 'slug',
	),
);
query_posts($myquery);

This gets us anything with a category of foo OR a tag of bar. Note that the relation is global to the query, so it appears outside the arrays in the tax_query, but still in the tax_query array itself. For clarity, I recommend always putting it first.

Useful Gallery Example

By combining these in different ways, you can make complex queries. What’s more, you can use it with any taxonomy you like. Here’s one I recently used:

$galleryquery = wp_parse_args($query_string);
$galleryquery['tax_query'] = array(
	'relation' => 'OR',
	array(
		'taxonomy' => 'post_format',
		'terms' => array('post-format-gallery'),
		'field' => 'slug',
	),
	array(
		'taxonomy' => 'category',
		'terms' => array('gallery'),
		'field' => 'slug',
	),
);
query_posts($galleryquery);

This gets any posts in either the gallery category OR that have a gallery post-format. Handy for making a gallery page template. I used the wp_parse_args($query_string) trick to make it able to handle paging properly, among other things.

Speed Concerns

Advanced taxonomy queries are cool, but be aware that complex queries are going to be slower. Not much slower, since the code does attempt to do things smartly, but each taxonomy you add is the equivalent of adding a JOIN. While the relevant tables are indexed, joins are still slower than non-joins. So it won’t always be a good idea to build out highly complex queries.

Still, it’s better than rolling your own complicated code to get a lot of things you don’t need and then parsing them out yourself. A whole lot easier too.

Shortlink:

184 Comments

  1. Nice article — being a slightly out of date wordpress hacker, I was mystified with the sudden taxonomy API in place of the rather simplistic categories API from days of old.

  2. Sweet! Is has_term() going to work now as well? I haven’t found a good way to check if a post or page has a custom taxonomy and run code if it does. I was told 3.1 is supposed to have has_term() which would work just like has_tag().

    • Yes, it’s in there. You can use it as has_term($term, $taxonomy). An optional third parameter is the post object or ID number, for use outside the loop.

      However, this is really just a prettified wrapper around the is_object_in_term function, which does sorta the same thing and has since 2.7.

  3. Nice writeup, Otto.

    One thing you didn’t mention though, is the shorthand syntax:

    query_posts(array(
    ‘tag’ => ‘foo+bar’,
    ‘category_name’ => ‘baz’,
    ));

    That is transformed into this:

    query_posts(array(
    ‘tax_query’ => array(
    array(
    ‘taxonomy’ => ‘post_tag’,
    ‘terms’ => array(‘foo’),
    ‘field’ => ‘slug’,
    ‘operator’ => ‘IN’,
    ),
    array(
    ‘taxonomy’ => ‘post_tag’,
    ‘terms’ => array(‘bar’),
    ‘field’ => ‘slug’,
    ‘operator’ => ‘IN’,
    ),
    array(
    ‘taxonomy’ => ‘category’,
    ‘terms’ => array(‘baz’),
    ‘field’ => ‘slug’,
    ‘operator’ => ‘IN’,
    ),
    )
    ));

    • The options of tag and category_name and cat and such are not new. They have existed for a long time. The only difference is now they are converted, internally, to these taxonomy queries, instead of being processed separately.

      In other words, that’s not so much “shorthand” as it is “backwards compatibility”. The + thing for multiple tags isn’t new either, it’s just been fixed to work correctly now. 🙂

      • The thing that is new is that you can have both category_name and tag query vars and get the correct results. Previously, it ignored one or the other.

        • Ah, yes. I make a brief mention of that at the top of the post, although without detailing it to that level. There were cases where “cat” and “tag” could be used together, but sometimes it didn’t work for no reason. Now that they’re being converted to this internally, they should all work properly.

  4. Very cool. Thanks for the heads-up, and the intel. I had never used queries until recently. Just never needed em, my WP install was pretty standard. I’ve only recently started doing things that required various queries. So it’s cool to get this new info while the rest of the stuff I just learned is stil fresh in my mind

  5. I’ve really been meaning to check out this functionality but haven’t had the chance. I was wondering if we will be able to query posts in multiple taxonomies directly from the url. Will something like the following work out of the box? /?taxonomy=term&taxonomy2=term2

  6. Nice post. Thanks for making the effort to explain how to use it so we don’t have to work as hard at it. 🙂

  7. This is great info! My ways of setting up queries has always been much more (and needlessly) complex. Thanks for detailing this out so well…

  8. Well, nice features. Thank you very much for detailed explanation.

  9. Good post… I’m going to take some of your examples and just dump them into anki for quick reference later.

  10. Reflecting on your saying; “but each taxonomy you add is the equivalent of adding a JOIN.”, I started thinking to see if there could be alternatives to the matter.

    Let’s build a basic scenario case first. Say, we have a category titled as “basketball”. And we also have 2 more custom taxonomies, called “places” and “people”. The site about sports, and you will have articles, such as one that talks about Kobe’s awesome finish at the staples center, or one that talks about another awesome finish from free-kicker Beckham at Wembley.

    So with the places and people taxonomies, you end up creating min 4 taxes,

    categories “basketball” and “soccer” ( 1st taxonomy )
    “people” taxonomy ( 2nd )
    “places” taxonomy ( 3rd )
    and finally tags taxonomy ( 4th )

    When posting an article, you do things such as categorizing the post under basketball and then tag it as “Staples Center” ( under the “Places Taxonomy” ) and tagging it as “Kobe” ( under the “People Taxonomy”) and finally tagging it as “Awesome Finishes” ( into regular tags taxonomy ).

    and when you need to pull posts, you have no choice but tap into those multiple taxonomies, that is tags, cats, places and people – causing us that expensive JOINs.

    How about not using the additional cus. taxes at all and going with some special tags instead… let’s see if the proposed trick worth the trouble!

    Th ideas is instead of creating two more taxonomies ( for “People” and “Places” ) and totally ending up with 4, we just work with 2, that are cats and tags. cats are obviously for the sports categories such as “basketball” and “soccer”. And the tags will be “awesome finishes”, “places_staples center” and “people_kobe”. The convention for the special tags would be TaxonomyContext_tag

    So, for the beckham post, those special tags will be “places_wembley” and “people_beckham”. It’s easy to work with.

    A few words on the _ in the tags.

    The “_” will help me in the display part of things.

    When I’m displaying site wide tags, I can still tap into the tag cloud but when I’m displaying the things, I just go with the right of the _. So I end up only seeing, “beckham” and “kobe” as opposed to “people_” in front of them.

    To get lists such as

    people: kobe, jordan
    places: staples center,

    we can ask WP to give us all tags that start with “people_” and “places_”.

    I’d like to hear your takes on this, especially in the light of; “each taxonomy you add is the equivalent of adding a JOIN.”

  11. Good point. But couldn’t that be taken care of again, thinking outside the box – as follows;

    As soon as we add a new “taxonomy_tag” structured term into the general tags list (such as people_kobe or people_jordan), we could run a SQL query to get the most updated list ( using that slower like_% query ) amd store it somewhere. Since this SQL would be executing at design time, and once per each tag change, WP thus visitors won’t be suffering from the like_%.

    With that in mind, problem may all come down to finding a space to store the HTML result of that query which is basically a clickable comma separated list of all tags starting “places_” prefix. The rest will be taken care of by CSS anyway.

    With this perspective, and keeping all the caching plug ins in mind, what do you say? Still go with extra custom taxonomy approach? What’s the greatest benefit we can achieve with custom taxonomies that would convince us to do those extra joins?

    • If you bring caching plugins to the table, you could do away with taxonomies entirely. You could do it all using custom fields! That doesn’t mean it would be an optimal solution.

      I really don’t see why you would go to all that trouble to simulate multiple taxonomies, when you already have native support for them. Just to avoid a few JOINs? Do some testing with the generated SQL queries and you’ll see it’s not so bad.

      • True. Also, the joins in this case are relatively fast, since all the relevant fields being joined are indexed. Using a LIKE or similar is problematic because that causes a full table scan, which is slow if you have big tables. But indexed joins can be done very efficiently in most cases.

  12. Agreed. But the site that I have to develop a WP solution for has a daily page view average above 200K. Not having the quantitative sense as to how much performance slow down these JOINs will bring to the table, day in day out is the why I’d like to explore/discuss alt. solutions.

    But looks like, you guys seem to go with the native, the straight-forward approach. Thanks for the replies. I appreciate it very much. /end of story.

  13. Great post, Otto.
    I am looking forward WP31 especially for the advanced taxonomy queries. Really good guide and explanation of using it.
    I have three questions about that:
    The first one is, how can the get_query_var() function be used to get a query var of a certain custom taxonomy or term?
    Will the parameters available to be fetched via this or via the $_GET ?
    The second is, what will be the url for such a page with multiple taxonomies? will it be rerwitten somehow to a nice permalink, or will it show a long GET parametered url?
    Last question is about theming – what template files in the hierarchy will handle these queries? what will be the specified template names of a specified query and to which general template files it will be call in the hierarchy?

    Thanks a lot for the great work done!

    Maor

    • The first one is, how can the get_query_var() function be used to get a query var of a certain custom taxonomy or term? Will the parameters available to be fetched via this or via the $_GET ?

      Most of the time you should use get_queried_object().

      If not, use $wp_query->tax_query->queries.

      You should never have to use $_GET.

      The second is, what will be the url for such a page with multiple taxonomies? will it be rerwitten somehow to a nice permalink, or will it show a long GET parametered url?

      Multiple taxonomy pages don’t have pretty URLs yet.

      what template files in the hierarchy will handle these queries? what will be the specified template names of a specified query and to which general template files it will be call in the hierarchy?

      The only template file loaded reliably will be taxonomy.php. It’s up to you to split it up further, as needed. It will also attempt to load {{{taxonomy-{first-tax}.php}}} which I guess will cause some trouble. Don’t know how to handle that yet.

  14. What does the ‘field’ do? I’m really only trying to query one taxonomy and order the resulting posts by another. Can I query using the ‘tax_query’ code above with only the ‘taxonomy’ => ‘mytax’ query? will it pull all the posts in that tax? or do I need to specify a term & field? I think thats a simple yes.

    My other question is a little more difficult. If I have all the posts assigned a custom ‘date’ tax (for events happening on that particular date), how can I order them by that date?
    I have this so far:

    $args['tax_query'] = array(
    	 array (
    		'taxonomy' => 'category',
    		'terms'  =>  'group_tours',
    		),
    	array (
    		'taxonomy' => 'tour_dates',
    	),
    	'orderby' => 'name',
    	'order' => 'asc',
    	);
    • 1. The field is what column you’re selecting on from the terms table. It can be “slug” or “name” or “term_id”. It defaults to “term_id”.

      So if you’re providing the query with slugs in your “terms” part of the query, then you need to specify that the field is slug. Note that your query as given won’t work because “group_tours” is not a term_id (which are integers). When you’re giving it a slug, you need to say so. It has to convert that slug into an ID number before it can be used.

      2. Both a taxonomy and terms are required. Leaving either of them out will make it result in a null query. No terms = no matches.

      3. Your order and orderby are not part of the tax_query argument. They are their own arguments to the main query.

      $args['tax_query'] = array(
      	array (...
      	)
      );
      $args['orderby'] = 'name';
      $args['order'] = 'asc';
      query_posts($args);
      
    • Sorry, I read your last question wrong.

      If I have all the posts assigned a custom ‘date’ tax (for events happening on that particular date), how can I order them by that date?

      You can’t. Don’t assign them dates in a taxonomy. That’s not what taxonomies are for. Taxonomies are for grouping, not for ordering by. If you want to order by something, it has to be something in the post itself, not a term in a taxo.

  15. Thanks very much for your reply. I realize after posting that the logic is wrong for ordering. But if I assign a term to a post, isn’t it then ‘part of the post’ and available (through some php magic) for ordering? Or by ‘part of the post’ – I can find which parts by looking in the post table, right?

    • Yeah, I meant the posts table. Ordering only works on those because that’s all the query is selecting, really. Selecting by a term or set of terms just provides which posts to select, it doesn’t provide the content of the terms for use in an orderby.

      If you’re making events as a custom post type or something, use the post_date to store the date of the event. Then you can order on it.

  16. Otto, Very helpful article as always. I have a question that’s slightly off topic and am hoping someone can direct me to a solution.

    WP 3.1 RC1 (and prior versions) of custom taxonomies set ‘hierarchical’ => true, won’t output a parent / child url structure. For example: if Europe is the parent and Germany the child, URLs are generated for {domain]/europe/ and [domain]/germany but not in the form [domain]/europe/germany/

    Is this intentional? Is there a way to address this issue to enable true hierarchical functionality as with categories?

  17. Otto,

    Thanks for your response. Just looked at that ticket and the subsequent ticket: http://core.trac.wordpress.org/ticket/15813 This issue may not have been fixed. There could still be a bug lurking as I’m running a fresh install of 3.1 RC1. I will figure out how to report this.

    Thanks again!

  18. Thanks Otto. I didn’t click that I needed to specify hierarchical in rewrite array as I was doing it in the primary register_taxonomy array. I appreciate your follow-up.

    Happy New Year!

  19. […] The same can be done with taxonomy queries. Instead of meta_query, however, use tax_query and instead of key, value, compare and type you would use taxonomy, terms, field and operator. Otto has a good explanation for that on his site. […]

  20. […] WordPress 3.1: Advanced Taxonomy Queries » Otto on WordPress […]

  21. […] Advanced Taxonomy & Postmeta Queries (dev) This is a developer feature that lets you drill down on taxaonomy and post meta than ever before with the WP_Query function. Read more. […]

  22. Otto, great article. I’ve got this working for a static page, but how could I make it dynamic based on user input. Ie if I have 3 taxonomies: Make, Model, and Color. Each with its own terms. I want the user to be able to select, via dropdown a term from each of the taxonomies and then submit a form. I want to capture this data on another page and list all posts that match the criteria. I tried to put this form in searchform.php to make it available throughout the site, but can’t make it work, no idea what I am doing wrong or if this is even possible. Any suggestions would be greatly appreciated. Thanks

  23. What’s the most reliable way of determining if the current request is a multi-taxonomy request? I want to write a wrapper function for my template, something like is_multi_tax().

    I’m currently using code that looks like this:

    if ( count( $wp_query->tax_query->queries ) > 1 ) return true; else return false;

    Is there a better way?

    John

  24. For some reason after query post when inside the loop the ‘the_title()’ displays all with taxonomy type ‘category’. ‘prof_type’ is a registered taxonomy that works in other parts of the site.
    What am i doing wrong? no php errors are thrown.


    $myquery['tax_query'] = array(
    array(
    'taxonomy' => 'prof_type',
    'field' => 'slug',
    'terms' => array('lawyer'),
    ),
    );
    query_posts($myquery);

    if ( have_posts() ) : while ( have_posts() ) : the_post();?>

  25. Nice !!!
    And how have I to put that code if I would like to have a form with checkboxes to fetch multiple taxonomies ?
    eg. I have 3 taxonomies
    yclad_category
    yclad_action
    yclad_tag.

    What should be the name of the form item ?
    (input type=”checkbox” name=”…”)

  26. and what about having several taxonomies in the url ?
    ../?taxonomy=yclad_action&term=action1&taxonomy=yclad_category&term=category1 do not seems to work…
    Any idea ?

    • You should take a look at this plugin:

      http://wordpress.org/extend/plugins/query-multiple-taxonomies/

      It doesn’t have a checkbox mode, but it has a dropdown mode, which should give you a start.

      • …I was using this one.
        But is it not the point of this new feature to have those functionnalities in the core ?
        Thanks.

        • The functionality (querying multiple taxonomies) is in Core. What’s left out is the UI.

          To answer your original question, there’s a shorthand syntax. For example, this URL:

          ?tax1=term1,term2&tax2=term2+term3

          would map to:

          query_posts( array(
            'tax1' => 'term1,term2',
            'tax2' => 'term3+term4'
          ) );
          

          which would further be converted to:

          query_posts( array(
            'tax_query' => array(
              array(
                'taxonomy' => 'tax1',
                'field' => 'slug',
                'terms' => array('term1', 'term2'),
                'operator' => 'OR'
              ),
              array(
                'taxonomy' => 'tax2',
                'field' => 'slug',
                'terms' => array('term3', 'term4'),
                'operator' => 'AND'
              ),
          ) );
          
        • Still, getting checkboxes to map to such a URL isn’t so easy, but I plan to update the plugin to have that option, once WP 3.1 is released.

          • Nice ! that’s exactly what I was looking for and it is now working with radio inputs : input type=”radio” name=”yclad_action” value=…
            Now, I still would like to have it working with checkboxes.
            I don’t need an external plugin, I’m making my own so I would just like to know if it possible to add a filter so I could transform an array into a string.
            my checkboxes would be

            input type=”checkbox” name=”yclad_action[]” value=”action1″
            input type=”checkbox” name=”yclad_action[]” value=”action2″

            and a filter would transform the array into yclad_action=action1,action2.
            Any idea ?

  27. This works great for wp 3.1 but all my results are limited to 10 results. I tried adding posts_per_page:


    $myquery['tax_query'] = array(
    'posts_per_page' => -1,
    'relation' => 'AND',
    array(
    'taxonomy' => 'genre',
    'terms' => array($_POST['genre']),
    ),
    array(
    'taxonomy' => 'region',
    'terms' => array($_POST['region']),
    ),
    );

    query_posts($myquery);

    How do you get unlimited results?

  28. Either this guide and Codex are misleading or I’m doing something wrong that I’m not aware of it.

    Goal: get all posts from parent term.

    Code:

    $terms = get_the_terms( $post->ID, 'my_taxonomy' );
    foreach( $terms as $term ) {
    $arguments = array(
    'post_type' => 'my_post_type',
    'tax_query' => array(
    'taxonomy' => 'my_taxonomy',
    'field' => 'id',
    'terms' => $term->term_id,
    )
    );
    $title = $term->name;
    }
    $sibling_posts = get_posts( $arguments );
    $archives = '';
    foreach( $sibling_posts as $sibling_post ) :
    $archives .= 'ID ) . '">' . get_the_title( $sibling_post->ID ) . '';
    endforeach;

    But I always get all my_post_type posts, it’s like ‘tax_query’ is completely ignored. I tried query_posts, different codes but nothing works.

    So who is wrong: me or these guides?

  29. I am creating a form for a user to select 1 term from each of 3 unique taxonomies. I want this user input to be directed to a page I have created that returns custom posts that match user input. What would the action of the form =? I can get it to work if the form action=bloginfo(‘url’), but I want a very customized page to view results. The page is custom_search.php in my theme folder. For the form action I am using [soucrecode language=”php”]/custom_search.php[/sourcecode]
    custom_search.php code is

    <?php
    /*
     * 
     */
    
    get_header();
    
    $type = $_GET['activity_type'];
    $season = $_GET['activity_season'];
    $setting = $_GET['activity_setting'];
    
    $myquery['post_type'] = 'activity';
    
    $myquery['tax_query'] = array(
    	array(
    		'taxonomy' => 'activity_season',
                    'terms' => array($season),
    		'field' => 'slug',
    	),
            array(
    		'taxonomy' => 'activity_type',
    		'terms' => array($type),
    		'field' => 'slug',
    	),
            array(
    		'taxonomy' => 'activity_setting',
    		'terms' => array($setting),
    		'field' => 'slug',
    	),
    );
    query_posts($myquery);
    
    ?>
    <div id="container">
        <div id="content">
            <?php if (have_posts()) : while (have_posts()) :
                    the_post(); ?>
                    <div id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
                        <h1 class="entry-title"><?php the_title(); ?></h1>
                        <div class="entry-content">
                            -----custom stuff------
                    <?php the_content(); ?>
                </div><!-- .entry-content -->
            </div><!-- #post-<?php the_ID(); ?> -->
            <?php endwhile;
                endif; ?>
            </div><!-- #content -->
        </div><!-- #container -->
    <?php get_sidebar(); ?>
    <?php get_footer(); ?>
    ?>
    

    Is this possible? I am not looking for a plugin, but rather the proper way of making this happen. Thanks.

  30. […] taxonomy, we can now query posts about ‘Automattic’ AND not about ‘Matt’.  Otto has put up a nice post to go into further detail on multiple taxonomy queries.  Are you imagining like I am all the fun […]

  31. Otto, your posts and tutorials own my WordPress bookmarks section and I have used them as a reference for quite some time. With all that you, and so many countless others, have available on the web I’ve never thought to ask anything of you, but here goes.

    I have a tiny events section I’m highlighting at the top of my website (in development) and the one thing that I cannot for the life of me figure out is this:

    I have a query that calls for five posts into a loop, future and published, then sorts them in ascending order — simple.

    array( ‘publish’, ‘future’ ),
    ‘posts_per_page’ => 4,
    ‘order’ => ‘ASC’,
    ‘category_name’ => ‘Event’));
    if ( have_posts() ) : while ($event_query->have_posts()) : $event_query->the_post();
    ?>

    The (seemingly) complicated part is I don’t want any posts to appear which are equal to OR previous to the viewer’s current time, only those posts which are current by the minute to upcoming posts should be visible. Only post from right now to future posts should be displayed.

    I’ve worked (and failed) too many times to count — I can’t find a solution anywhere else, and frankly at this point I wish I’d asked sooner.
    Any ideas?

  32. […] 3.1 will come with powerful taxonomy querying […]

  33. Wow, I’m very pleased to discover that you can even combine meta_query and tax_query in one query, like so:


    $query = array(
    'post_type' => 'ad',
    'meta_query' => array(
    array(
    'key' => 'status',
    'value' => 'Active',
    'compare' => 'LIKE'
    )
    ),
    'tax_query' => array(
    array(
    'taxonomy' => 'size',
    'terms' => array( 'box' ),
    'field' => 'slug'
    )
    )
    );

    These two additions to WordPress 3.1 have really opened up an exciting range of possibilities.

  34. Hey,
    I’m not anywhere near being a PHP expert and im not sure if i understood everything in your article correctly… I have created a custom post type called “Projects” and later registered a taxonomy used in said post type called “project-category”. What im trying to do is list only Projects in specific project categories…
    Heres what I came up with:


    $query = array(
    'post_type' => 'projects',
    'orderby' => 'menu_order',
    'nopaging' => true,
    'tax_query' => array(
    'taxonomy' => 'project-category',
    'terms' => array('interaktywne', 'multimedialne', 'wideo', 'inne'),
    'field' => 'slug'
    ),
    );
    query_posts($query);

    The reason im posting this is probably obvious.. Its not working! And in an act of desperation im seeking help here 😛
    Any ideaS?

    Thanks in advance,
    P.

    • I don’t see anything wrong with your query. Make sure you spelled everything correctly. Also remember that capitalization matters.

      • Hi again,
        Thanks for your quick reply. Im pretty sure its not a typo, i went through all the names a good few times, and found nothing.. Ill post the bits of code registering the post type and taxonomies, maybe that will shed some light on the problem.

        Post type:

          register_post_type( 'projects',
            array(
              'labels' => array(
                'name' => __( 'Projekty' ),
                'singular_name' => __( 'Projekt' )
              ),
              'public' => true,
              'has_archive' => true,
        	  'hierarchical' => true,
              'rewrite' => array('slug' => 'projects'),
        	  'supports' => array('title','editor','thumbnail','excerpt')
            )
          );

        Taxonomy:

        	register_taxonomy('project-category',array('projects'), array(
        		'hierarchical' => true,
        		'labels' => $labels,
        		'show_ui' => true,
        		'query_var' => true,
        		'rewrite' => array( 'slug' => 'project-category' ),
        ));

        And once again, the query ( i changed ‘field’ to id, to avoid typos ):

        			$query = array(
        				'tax_query' => array(
        					'taxonomy' => 'project-category',
        					'terms' => array(3, 4, 5, 6),
        					'field' => 'id',
        				),
        				'post_type' => 'projects',
        				'orderby' => 'menu_order',
        				'nopaging' => true,				
        			);
        			query_posts($query);
        
    • It’s not working because you have to nest it in another array:

      ‘tax_query’ => array(
      array(
      ‘taxonomy’ => ‘project-category’,
      ‘terms’ => array(‘interaktywne’, ‘multimedialne’, ‘wideo’, ‘inne’),
      ‘field’ => ‘slug’
      )
      )

      Since it’s a single taxonomy you’re dealing with, it would be easier to write it like this:

      ‘project-category’ => ‘interaktywne,multimedialne,wideo,inne’

  35. Thanks for putting this tutorial together, Otto. Is it also possible to make a query where I can dynamically call a list of terms from one taxonomy based on the terms from a second taxonomy? I’ve been trying to figure this out for about 4 months now, but i can barely wrap my head around the idea, lol.

    Say I have the taxonomy “People” (with terms like W Shatner, J Carrey and D Chappelle) and “Places” (with terms like Canada and USA) I’d like to be able show the terms “W Shatner” and “J Carrey” in a group because posts with those terms also have the place term “Canada” (where “D Chappelle” wouldn’t show because posts with that term have the place “USA”).

    I thought the multiple taxonomies would do the trick here, but it doesn’t unfortunately (unless I missed something?)

    Thanks in advance

  36. Scratching my head on this one…
    Trying to display a list of accomodation, using 3 taxonomies.
    I have Country, Acc-type, Region
    ‘country’ =>$termcountry->name is picked up higher on the page

    Help please

    $taxonomy2 = 'accomodation-type';
    //Find the terms in Accomodation type and loop through it

    $termsacc = get_terms("accomodation-type",array('orderby' => 'slug', 'order' => 'ASC'));

    foreach ($termsacc as $termaccomodation) {
    $taxonomyregion = 'region';
    $termsreg = get_terms("region",array('orderby' => 'slug', 'order' => 'ASC'));

    foreach ($termsreg as $termregion) {

    $query = array(
    'post_type' => 'accomodation',
    'taxonomy' => 'accomodation-type',
    'country' =>$termcountry->name,
    'orderby' => 'slug',
    'order' => 'ASC',
    'relation' => 'AND',
    'tax_query' => array(
    array(
    'taxonomy' => $taxonomyregion,
    'field' => 'slug',
    'terms' => $termregion->name,
    'operator' => 'IN',
    )
    )
    );

    query_posts($query);

  37. Having trouble with the following code. Everything seems to be working properly outside of the fact that it is returning posts that are not tagged with the tag ‘resource’. I have also posted on the WP forums but since this page was instrumental in getting this far I figured that it may be a the better place to ask my question.


    $pagesorter['tax_query'] = array(
    'taxonomy' => 'post_tag',
    'terms' => 'resource',
    'field' => 'slug',
    array(
    'taxonomy' => 'category',
    'terms' => $cat,
    'field' => 'id',
    ),
    array(
    'taxonomy' => 'category',
    'terms' => $topic,
    'field' => 'id',
    ),
    );

    I also tried nesting arrays in arrays with one focusing on Category and the other on Tag and still had no luck. It is a bit over my head. However, as mentioned the above code is returning post that are in the correct categories but the posts are NOT tagged at all and they are still displaying when I only want them to display the properly tagged posts within selected/queried categories. Also tried to just put 'tag' => 'resource', right into the query both within the parent array and the child arrays and it did not seem to work either.

    Thanks for the time and thanks for the article!

  38. […] comment at this time.  Aina Lluna, SEO, wordpress, google analytics, soc… Take a look http://ottopress.com/2010/wordpr…This answer .Please specify the necessary improvements. Edit Link Text Show answer summary […]

  39. Hello. I have been scratching my head for a little while on this one. I have a custom post called “Slider” and a taxonomy called “Slider Categories”. Depending on the page I would like to display a slider based on the Slider Category.

    Here is my code.


    /THE FUNCTION THAT WILL OUTPUT THE SLIDER
    function slideshow($slider_category) {
    /* Query from the database. */
    $loop = new WP_Query(
    array(
    'post_type' =>'slider',
    'orderby' =>'ID',
    'order' =>'ASC',
    'tax_query' => array(
    array(
    'taxonomy' => $slider_category,
    'field' => 'id'
    ) )
    )
    );

    And then on the page template I would add:

    However this is not working. Any suggestions? 🙂

  40. How can I get all posts, which does not have specific meta_key:
    Something like this:

    $args = array(
    'meta_key' => 'my_key',
    'meta_value' => 'my_value',
    'meta_compare' => '!='
    );
    query_posts( $args );

    but posts where the meta_value is not set at all.

    • If you want to find where the meta value is the empty string, set the meta_value to the empty string of ”, and set meta_compare to ‘=’. The meta_value is actually trimmed before the query, so having it be a space will be non-empty, but it will get queried as the empty string.

      If you want to find where the meta_value is NULL, then that cannot be done with this sort of query. The meta_compare does not support the IS NULL and IS NOT NULL types. However, this shouldn’t be needed, the empty string is used for blank meta values, in general.

      If you want to query for posts that don’t have a particular meta_key defined, then that also cannot be done with this type of query. The meta_key is used on a JOIN, so you can’t join the table to look for posts without it.

      Advanced queries are not meant to cover all possible cases, just the most common ones. You should redesign your meta fields to accommodate the limitations.

  41. […] I knew that this should be relatively easy to accomplish. WordPress 3.1 extended the way that taxonomies can be queried which allows for a great deal of fine tuning. Read more about Advanced Taxonomy Queries in WordPress. […]

  42. Ok so i’ve got my query pulling in posts from a custom taxonomy named children and it seems to be pulling in the posts from each kid that i have in the taxonomy, but the sort is still independent between each kid. how do i sort my posts by post date reguardless of which taxonomy they’re in? here’s my code right now.

    $tax = 'children';
    $tax_terms = get_terms( $tax );
    if ($tax_terms) {
    	foreach ($tax_terms  as $tax_term) {
    	$args = array(
    		
    		"$tax" => $tax_term->slug,
    		'post_type'       => 'post',
    		'post_status' => 'publish',
    		'posts_per_page' => -1,
    		'caller_get_posts'=> 1,
    
    
    		
    	);
    
    		$my_query = null;
    		$my_query = new WP_Query($args);
    
    		if( $my_query->have_posts() ) : ?>
    
    		<?php while ( $my_query->have_posts() ) : $my_query->the_post(); ?>
    
  43. […] – @scribu to the rescue! — maybe. Kept searching, and this is what I found. Thought I had it all figured, but I […]

  44. Hi! I´m having a bit of trouble trying to turn a Select query into a wordpress custom query.
    The case is i have three custom types and a taxonomy that affects only one of them
    I would like to list all the posts on the first two custom types and only the ones with taxonomy term from the last post type.

    This sql would do it

    SELECT * FROM $wpdb->posts
    LEFT JOIN $wpdb->postmeta ON($wpdb->posts.ID = $wpdb->postmeta.post_id)
    LEFT JOIN $wpdb->term_relationships ON($wpdb->posts.ID = $wpdb->term_relationships.object_id)
    LEFT JOIN $wpdb->term_taxonomy ON($wpdb->term_relationships.term_taxonomy_id = $wpdb->term_taxonomy.term_taxonomy_id)
    LEFT JOIN $wpdb->terms ON($wpdb->terms.term_id = $wpdb->term_taxonomy.term_id)
    WHERE ($wpdb->terms.name = 'Mostrar'
    AND $wpdb->term_taxonomy.taxonomy = 'condicion'
    AND $wpdb->posts.post_status = 'publish'
    AND $wpdb->posts.post_type = 'aroztegui')
    OR
    ($wpdb->posts.post_status = 'publish'
    AND $wpdb->posts.post_type = 'investigacion')
    OR
    ($wpdb->posts.post_status = 'publish'
    AND $wpdb->posts.post_type = 'extension')
    GROUP BY $wpdb->posts.ID

    But either i need to make this one work with wp_pagenavi or turn it into a custom query

    Any help would be much appreciated.

    • You can’t do that with a native query. Taxonomy parameters affect the whole query. If you want to do it with native WP_Query objects, you’ll need to make two queries and combine the results somehow.

      • Thanks Otto.
        I guess i´ll go this way $pageposts = $wpdb->get_results($querystr, OBJECT) where $querystr is the SQL i sent before.
        But even in that way, there´s something that´s not quite ok.
        i´m having some trouble with paging.
        Even when wp-pagenavi is now working, it shows more pages than there really are.

        If any ideas… they´re welcome.

        Thanks again and have a great time.

  45. So i’m not much of a coder myself, but i was able to get my taxonomy query working and pagination as well. in an attempt to help anyone else with the same problem I had, here’s my code.

    <?php
    
    // List posts by the terms for a custom taxonomy of any post type
    $paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
    
    $args = array(
    'paged'=>$paged,
    	'tax_query' => array(
    		array(
    			'taxonomy'=>'children',
    			'field'=>'slug',
    			'paged'=>$paged,
    			'posts_per_page'=>-1,
    			'terms'=>get_terms('children','fields=names')
    		)
    	)
    );
    
    		$temp = $wp_query;
    		$wp_query = new WP_Query($args);
    			$wp_query->query($args);
    			
    		if( $wp_query->have_posts() ) : ?>
    
    		<?php while ( $wp_query->have_posts() ) : $wp_query->the_post(); ?>
    

    Thanks for everyone who helped.

  46. […] to Otto‘s excellent post on advanced taxonomy queries, I (mostly) understand how to create multi-taxonomy and multi-term queries. However, what I […]

  47. First site to tell me how to exclude posts from a given taxonomy. Thanks!

Leave a Reply to Peter Cancel reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Need to post PHP code? Wrap it in [php] and [/php] tags.