WordPress.org

Make WordPress Core

Opened 14 months ago

Last modified 4 weeks ago

#47802 new defect (bug)

WP_Query get_posts sometimes inserts contradictory taxonomy queries

Reported by: nstevensdc Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version:
Component: Query Keywords:
Focuses: Cc:

Description

In our website, we have a custom taxonomy called 'section.' It figures prominently in a query we're doing. Here's a (lightly anonymized) dump of the array we pass to the WP_Query constructor, before calling get_posts() on the query created:

    [posts_per_page] => 25
    [post_type] => post
    [tax_query] => Array
        (
            [0] => Array
                (
                    [operator] => IN
                    [taxonomy] => section
                    [field] => slug
                    [terms] => Array
                        (
                            [0] => section1
                            [1] => section2
                            [2] => section3
                            [3] => section4
                        )

                )

            [1] => Array
                (
                    [operator] => NOT IN
                    [taxonomy] => section
                    [field] => slug
                    [terms] => Array
                        (
                            [0] => section5
                        )

                )

            [relation] => AND
        )

    [orderby] => post_date
    [order] => DESC

We had a problem where many posts that should be returned here, are not. After much digging, I found the problem in get_posts. specifically in the segment of code commented:

			/*
			 * Set 'taxonomy', 'term', and 'term_id' to the
			 * first taxonomy other than 'post_tag' or 'category'.
			 */

It's true, the code is doing just that. It's adding a taxonomy parameter. But it's picking up one of the sections that is supposed to be one of several possibilities the section can be IN, not required. It then makes that one single section required in an appended AND! Sample (again, lightly anonymized) SQL segment of the obviously self-contradictory conditions created (where 1001-1005 are the IDs corresponding to sections 1-5)

  wp_term_relationships.term_taxonomy_id IN (1001, 1002, 1003, 1004)
  AND
  wp_posts.ID NOT IN (
				SELECT object_id
				FROM wp_term_relationships
				WHERE term_taxonomy_id IN (1005)
			)
  AND
  tt1.term_taxonomy_id IN (1001)
)

That third clause, the final IN (1001) is being added by get_posts in the above-commented section. That's why we only get a fraction of the posts we're supposed to get. It's effectively overriding most of our IN list!

I can work around this by prepending to my tax_query an effective no-op using some custom taxonomy, but this seems like a problem that should be addressed in the WP core.

thank you,

Change History (2)

#1 @SergeyBiryukov
14 months ago

  • Component changed from General to Query

#2 @krypsin
4 weeks ago

I believe this is an issue that I'm also experiencing.

The thing that I've noticed is that the query works when using term_id as the field value but not slug.
Using slug is what seems to be adding the additional AND is coming from.

So, the following (using slug) does not work:

Args dump:

array(3) {
  ["course_type"]=>
  array(1) {
    [0]=>
    array(4) {
      ["taxonomy"]=>
      string(11) "course_type"
      ["field"]=>
      string(4) "slug"
      ["terms"]=>
      array(2) {
        [0]=>
        string(30) "coursetype1"
        [1]=>
        string(20) "coursetype2"
      }
      ["compare"]=>
      string(2) "IN"
    }
  }
  ["location"]=>
  array(1) {
    [0]=>
    array(4) {
      ["taxonomy"]=>
      string(8) "location"
      ["field"]=>
      string(4) "slug"
      ["terms"]=>
      array(1) {
        [0]=>
        string(23) "location1"
      }
      ["compare"]=>
      string(2) "IN"
    }
  }
  ["relation"]=>
  string(3) "AND"
}

SQL:

SELECT SQL_CALC_FOUND_ROWS  wp_posts.ID FROM wp_posts  LEFT JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id)  LEFT JOIN wp_term_relationships AS tt1 ON (wp_posts.ID = tt1.object_id)  LEFT JOIN wp_term_relationships AS tt2 ON (wp_posts.ID = tt2.object_id)
WHERE 1=1 AND (
    (
        wp_term_relationships.term_taxonomy_id IN (650,929)
    )
    AND
    (
        tt1.term_taxonomy_id IN (1059)
    )
    AND
    tt2.term_taxonomy_id IN (929)
) AND wp_posts.post_type = 'course' AND ((wp_posts.post_status = 'publish')) GROUP BY wp_posts.ID ORDER BY wp_posts.post_date DESC LIMIT 0, 10

Whereas using term_id does:

Args dump:

array(3) {
  ["course_type"]=>
  array(1) {
    [0]=>
    array(4) {
      ["taxonomy"]=>
      string(11) "course_type"
      ["field"]=>
      string(7) "term_id"
      ["terms"]=>
      array(2) {
        [0]=>
        string(3) "929"
        [1]=>
        string(3) "650"
      }
      ["compare"]=>
      string(2) "IN"
    }
  }
  ["location"]=>
  array(1) {
    [0]=>
    array(4) {
      ["taxonomy"]=>
      string(8) "location"
      ["field"]=>
      string(7) "term_id"
      ["terms"]=>
      array(1) {
        [0]=>
        string(4) "1059"
      }
      ["compare"]=>
      string(2) "IN"
    }
  }
  ["relation"]=>
  string(3) "AND"
}

SQL

SELECT SQL_CALC_FOUND_ROWS  wp_posts.ID FROM wp_posts  LEFT JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id)  LEFT JOIN wp_term_relationships AS tt1 ON (wp_posts.ID = tt1.object_id)
WHERE 1=1 AND (
    (
        wp_term_relationships.term_taxonomy_id IN (650,929)
    )
        AND
    (
        tt1.term_taxonomy_id IN (1059)
    )
) AND wp_posts.post_type = 'course' AND ((wp_posts.post_status = 'publish')) GROUP BY wp_posts.ID ORDER BY wp_posts.post_date DESC LIMIT 0, 10
Note: See TracTickets for help on using tickets.