1

I'm working with a tag plugin. Is there a WordPress way to get_posts or query all posts that have no tags?

EDIT

I had already queried the WP Codex, search Stackexchange and Google for a related question at the time of the question.

I found several results for helping finding tags, but not the NOT IN operator in a tax_query. I didn't have any code to share yet as I didn't have the information I needed to build the query $args.

0

3 Answers 3

8

A WP_Query where 'tax_query' has all tag terms and operator 'NOT IN':

$tags = get_terms('post_tag', array('fields'=>'ids') );
$args = array(
  'post_type' => 'post',
  'posts_per_page' => -1,
  'tax_query' => array(
    array(
      'taxonomy' => 'post_tag',
      'field' => 'id',
      'terms' => $tags,
      'operator' => 'NOT IN'
    )
  )
);
$untagged = new WP_Query( $args );
2
  • I found that the acknowledged solution only works if the post is tagged with at least one tag (in a different taxonomy, obviously). If it is not, it doesn't seem to appear in the query results.
    – user70493
    Apr 10, 2015 at 21:43
  • To invert that, which was what I was looking for, to exclude all untagged posts the tax_query simply needs terms to have an empty string passed to it and the operator set to IN like so: array('taxonomy' => 'post_tag', 'field' => 'id', 'terms' => ' ', 'operator' => 'IN').
    – leymannx
    Apr 2, 2019 at 20:11
1

Not exactly answers the question, but might be a better solution than the above when dealing with bigger databases

// Get all posts IDs - Query took 0.0030 seconds
$all_posts_ids_query = $wpdb->prepare( "SELECT $wpdb->posts.ID FROM $wpdb->posts WHERE $wpdb->posts.post_type = '%s' GROUP BY $wpdb->posts.ID", 'your-post-type' );

$all_posts_ids = $wpdb->get_col( $all_posts_ids_query );


// Get all posts IDs that has tags - Query took 0.0300 seconds
$all_posts_ids_with_tags_query = $wpdb->prepare( "SELECT $wpdb->posts.ID FROM $wpdb->posts
    INNER JOIN $wpdb->term_relationships ON ($wpdb->posts.ID = $wpdb->term_relationships.object_id)
    INNER JOIN $wpdb->term_taxonomy ON ($wpdb->term_relationships.term_taxonomy_id = $wpdb->term_taxonomy.term_taxonomy_id)
    WHERE 1=1 AND ( $wpdb->term_taxonomy.taxonomy = '%s' )
    AND $wpdb->posts.post_type = '%s' GROUP BY $wpdb->posts.ID", 'your-taxonomy-name', 'your-post-type' );

$all_posts_ids_with_tags = $wpdb->get_col( $all_posts_ids_with_tags_query );


// Diff IDs arrays to get posts without tags
$all_posts_ids_without_tags = array_diff( $all_posts_ids, $all_posts_ids_with_tags );

DB size this was tested on (quite small but may give some representation), posts: ~3.000, terms: ~1.000, term relationships: ~6.000

1
<?php
  $args = array(
    'post_type' => 'post',
    'posts_per_page' => -1,
    'tax_query' => array(
        array(
            'taxonomy' => 'post_tag',
            'field' => 'id',
            'terms' => 'null',
            'operator' => 'NOT IN'
    )));
  $untagged = new WP_Query( $args );?>
2
  • This may be the right answer (I haven't checked) But you need to add explanation as well.
    – rudtek
    Apr 4, 2019 at 16:15
  • This is the right answer May 26, 2020 at 17:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.