Overhaul SQL generating logic in WP_Meta_Query to avoid unnecessary table joins.
The logic used to generate clause SQL in `WP_Meta_Query` is somewhat arcane, stemming mostly from an ongoing effort to eliminate costly table joins when they are not necessary. By systematizing the process of looking for shareable joins - as was done in `WP_Tax_Query` [29902] - it becomes possible to simplify the construction of SQL queries in `get_sql_for_clause()`. Moreover, the simplified logic is actually considerably better at identifying shareable joins, such that certain uses of `WP_Meta_Query` will see joins reduced by 50% or more. Includes integration tests for a representative cross-section of the query clause combinations that result in shared table aliases. Props boonebgorges, sc0ttkclark. See #24093. git-svn-id: https://develop.svn.wordpress.org/trunk@29940 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
parent
be59c5009e
commit
1708a580fd
@ -987,6 +987,10 @@ class WP_Meta_Query {
|
||||
|
||||
// First-order clause.
|
||||
} else if ( $this->is_first_order_clause( $query ) ) {
|
||||
if ( isset( $query['value'] ) && array() === $query['value'] ) {
|
||||
unset( $query['value'] );
|
||||
}
|
||||
|
||||
$clean_queries[] = $query;
|
||||
|
||||
// Otherwise, it's a nested query, so we recurse.
|
||||
@ -1167,7 +1171,12 @@ class WP_Meta_Query {
|
||||
* }
|
||||
*/
|
||||
protected function get_sql_clauses() {
|
||||
$sql = $this->get_sql_for_query( $this->queries );
|
||||
/*
|
||||
* $queries are passed by reference to get_sql_for_query() for recursion.
|
||||
* To keep $this->queries unaltered, pass a copy.
|
||||
*/
|
||||
$queries = $this->queries;
|
||||
$sql = $this->get_sql_for_query( $queries );
|
||||
|
||||
if ( ! empty( $sql['where'] ) ) {
|
||||
$sql['where'] = ' AND ' . $sql['where'];
|
||||
@ -1195,7 +1204,7 @@ class WP_Meta_Query {
|
||||
* @type string $where SQL fragment to append to the main WHERE clause.
|
||||
* }
|
||||
*/
|
||||
protected function get_sql_for_query( $query, $depth = 0 ) {
|
||||
protected function get_sql_for_query( &$query, $depth = 0 ) {
|
||||
$sql_chunks = array(
|
||||
'join' => array(),
|
||||
'where' => array(),
|
||||
@ -1211,7 +1220,7 @@ class WP_Meta_Query {
|
||||
$indent .= " ";
|
||||
}
|
||||
|
||||
foreach ( $query as $key => $clause ) {
|
||||
foreach ( $query as $key => &$clause ) {
|
||||
if ( 'relation' === $key ) {
|
||||
$relation = $query['relation'];
|
||||
} else if ( is_array( $clause ) ) {
|
||||
@ -1278,7 +1287,7 @@ class WP_Meta_Query {
|
||||
* @type string $where SQL fragment to append to the main WHERE clause.
|
||||
* }
|
||||
*/
|
||||
public function get_sql_for_clause( $clause, $parent_query ) {
|
||||
public function get_sql_for_clause( &$clause, $parent_query ) {
|
||||
global $wpdb;
|
||||
|
||||
$sql_chunks = array(
|
||||
@ -1286,16 +1295,13 @@ class WP_Meta_Query {
|
||||
'join' => array(),
|
||||
);
|
||||
|
||||
$i = count( $this->table_aliases );
|
||||
$alias = $i ? 'mt' . $i : $this->meta_table;
|
||||
|
||||
if ( isset( $clause['compare'] ) ) {
|
||||
$meta_compare = strtoupper( $clause['compare'] );
|
||||
$clause['compare'] = strtoupper( $clause['compare'] );
|
||||
} else {
|
||||
$meta_compare = isset( $clause['value'] ) && is_array( $clause['value'] ) ? 'IN' : '=';
|
||||
$clause['compare'] = isset( $clause['value'] ) && is_array( $clause['value'] ) ? 'IN' : '=';
|
||||
}
|
||||
|
||||
if ( ! in_array( $meta_compare, array(
|
||||
if ( ! in_array( $clause['compare'], array(
|
||||
'=', '!=', '>', '>=', '<', '<=',
|
||||
'LIKE', 'NOT LIKE',
|
||||
'IN', 'NOT IN',
|
||||
@ -1303,58 +1309,62 @@ class WP_Meta_Query {
|
||||
'EXISTS', 'NOT EXISTS',
|
||||
'REGEXP', 'NOT REGEXP', 'RLIKE'
|
||||
) ) ) {
|
||||
$meta_compare = '=';
|
||||
$clause['compare'] = '=';
|
||||
}
|
||||
|
||||
/*
|
||||
* There are a number of different query structures that get
|
||||
* built in different ways.
|
||||
* 1. Key-only clauses - (a) clauses without a 'value' key that
|
||||
* appear in the context of an OR relation and do not use
|
||||
* 'NOT EXISTS' as the 'compare', or (b) clauses with an
|
||||
* empty array for 'value'.
|
||||
*/
|
||||
if ( ! empty( $clause['key'] ) && (
|
||||
( ! array_key_exists( 'value', $clause ) && 'NOT EXISTS' !== $meta_compare && 'OR' === $parent_query['relation'] ) ||
|
||||
( isset( $clause['value'] ) && is_array( $clause['value'] ) && empty( $clause['value'] ) )
|
||||
) ) {
|
||||
$meta_compare = $clause['compare'];
|
||||
|
||||
$alias = $this->meta_table;
|
||||
$sql_chunks['join'][] = " INNER JOIN $this->meta_table ON ($this->primary_table.$this->primary_id_column = $alias.$this->meta_id_column)";
|
||||
$sql_chunks['where'][] = $wpdb->prepare( "$this->meta_table.meta_key = %s", trim( $clause['key'] ) );
|
||||
// First build the JOIN clause, if one is required.
|
||||
$join = '';
|
||||
|
||||
// 2. NOT EXISTS.
|
||||
} else if ( 'NOT EXISTS' === $meta_compare ) {
|
||||
$join = " LEFT JOIN $this->meta_table";
|
||||
$join .= $i ? " AS $alias" : '';
|
||||
$join .= $wpdb->prepare( " ON ($this->primary_table.$this->primary_id_column = $alias.$this->meta_id_column AND $alias.meta_key = %s )", $clause['key'] );
|
||||
$sql_chunks['join'][] = $join;
|
||||
// We prefer to avoid joins if possible. Look for an existing join compatible with this clause.
|
||||
$alias = $this->find_compatible_table_alias( $clause, $parent_query );
|
||||
if ( false === $alias ) {
|
||||
$i = count( $this->table_aliases );
|
||||
$alias = $i ? 'mt' . $i : $this->meta_table;
|
||||
|
||||
$sql_chunks['where'][] = $alias . '.' . $this->meta_id_column . ' IS NULL';
|
||||
// JOIN clauses for NOT EXISTS have their own syntax.
|
||||
if ( 'NOT EXISTS' === $meta_compare ) {
|
||||
$join .= " LEFT JOIN $this->meta_table";
|
||||
$join .= $i ? " AS $alias" : '';
|
||||
$join .= $wpdb->prepare( " ON ($this->primary_table.$this->primary_id_column = $alias.$this->meta_id_column AND $alias.meta_key = %s )", $clause['key'] );
|
||||
$sql_chunks['join'][] = $join;
|
||||
|
||||
// 3. EXISTS and other key-only queries.
|
||||
} else if ( 'EXISTS' === $meta_compare || ( ! empty( $clause['key'] ) && ! array_key_exists( 'value', $clause ) ) ) {
|
||||
$join = " INNER JOIN $this->meta_table";
|
||||
$join .= $i ? " AS $alias" : '';
|
||||
$join .= " ON ( $this->primary_table.$this->primary_id_column = $alias.$this->meta_id_column )";
|
||||
$sql_chunks['join'][] = $join;
|
||||
// All other JOIN clauses.
|
||||
} else {
|
||||
$alias = $this->find_compatible_table_alias( $clause, $parent_query );
|
||||
if ( false === $alias ) {
|
||||
$alias = $i ? 'mt' . $i : $this->meta_table;
|
||||
|
||||
$sql_chunks['where'][] = $wpdb->prepare( $alias . '.meta_key = %s', trim( $clause['key'] ) );
|
||||
|
||||
// 4. Clauses that have a value.
|
||||
} else if ( array_key_exists( 'value', $clause ) ) {
|
||||
$join = " INNER JOIN $this->meta_table";
|
||||
$join .= $i ? " AS $alias" : '';
|
||||
$join .= " ON ($this->primary_table.$this->primary_id_column = $alias.$this->meta_id_column)";
|
||||
$sql_chunks['join'][] = $join;
|
||||
|
||||
if ( ! empty( $clause['key'] ) ) {
|
||||
$sql_chunks['where'][] = $wpdb->prepare( "$alias.meta_key = %s", trim( $clause['key'] ) );
|
||||
$join .= " INNER JOIN $this->meta_table";
|
||||
$join .= $i ? " AS $alias" : '';
|
||||
$join .= " ON ( $this->primary_table.$this->primary_id_column = $alias.$this->meta_id_column )";
|
||||
}
|
||||
}
|
||||
|
||||
$meta_type = $this->get_cast_for_type( isset( $clause['type'] ) ? $clause['type'] : '' );
|
||||
$this->table_aliases[] = $alias;
|
||||
$sql_chunks['join'][] = $join;
|
||||
}
|
||||
|
||||
$meta_value = isset( $clause['value'] ) ? $clause['value'] : '';
|
||||
// Save the alias to this clause, for future siblings to find.
|
||||
$clause['alias'] = $alias;
|
||||
|
||||
// Next, build the WHERE clause.
|
||||
$where = '';
|
||||
|
||||
// meta_key.
|
||||
if ( array_key_exists( 'key', $clause ) ) {
|
||||
if ( 'NOT EXISTS' === $meta_compare ) {
|
||||
$sql_chunks['where'][] = $alias . '.' . $this->meta_id_column . ' IS NULL';
|
||||
} else {
|
||||
$sql_chunks['where'][] = $wpdb->prepare( "$alias.meta_key = %s", trim( $clause['key'] ) );
|
||||
}
|
||||
}
|
||||
|
||||
// meta_value.
|
||||
if ( array_key_exists( 'value', $clause ) ) {
|
||||
$meta_value = $clause['value'];
|
||||
$meta_type = $this->get_cast_for_type( isset( $clause['type'] ) ? $clause['type'] : '' );
|
||||
|
||||
if ( in_array( $meta_compare, array( 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN' ) ) ) {
|
||||
if ( ! is_array( $meta_value ) ) {
|
||||
@ -1364,19 +1374,34 @@ class WP_Meta_Query {
|
||||
$meta_value = trim( $meta_value );
|
||||
}
|
||||
|
||||
if ( 'IN' == substr( $meta_compare, -2 ) ) {
|
||||
$meta_compare_string = '(' . substr( str_repeat( ',%s', count( $meta_value ) ), 1 ) . ')';
|
||||
} elseif ( 'BETWEEN' == substr( $meta_compare, -7 ) ) {
|
||||
$meta_value = array_slice( $meta_value, 0, 2 );
|
||||
$meta_compare_string = '%s AND %s';
|
||||
} elseif ( 'LIKE' == $meta_compare || 'NOT LIKE' == $meta_compare ) {
|
||||
$meta_value = '%' . $wpdb->esc_like( $meta_value ) . '%';
|
||||
$meta_compare_string = '%s';
|
||||
} else {
|
||||
$meta_compare_string = '%s';
|
||||
switch ( $meta_compare ) {
|
||||
case 'IN' :
|
||||
case 'NOT IN' :
|
||||
$meta_compare_string = '(' . substr( str_repeat( ',%s', count( $meta_value ) ), 1 ) . ')';
|
||||
$where = $wpdb->prepare( $meta_compare_string, $meta_value );
|
||||
break;
|
||||
|
||||
case 'BETWEEN' :
|
||||
case 'NOT BETWEEN' :
|
||||
$meta_value = array_slice( $meta_value, 0, 2 );
|
||||
$where = $wpdb->prepare( '%s AND %s', $meta_value );
|
||||
break;
|
||||
|
||||
case 'LIKE' :
|
||||
case 'NOT LIKE' :
|
||||
$meta_value = '%' . $wpdb->esc_like( $meta_value ) . '%';
|
||||
$where = $wpdb->prepare( '%s', $meta_value );
|
||||
break;
|
||||
|
||||
default :
|
||||
$where = $wpdb->prepare( '%s', $meta_value );
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
$sql_chunks['where'][] = $wpdb->prepare( "CAST($alias.meta_value AS {$meta_type}) {$meta_compare} {$meta_compare_string}", $meta_value );
|
||||
if ( $where ) {
|
||||
$sql_chunks['where'][] = "CAST($alias.meta_value AS {$meta_type}) {$meta_compare} {$where}";
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1387,10 +1412,62 @@ class WP_Meta_Query {
|
||||
$sql_chunks['where'] = array( '( ' . implode( ' AND ', $sql_chunks['where'] ) . ' )' );
|
||||
}
|
||||
|
||||
$this->table_aliases[] = $alias;
|
||||
|
||||
return $sql_chunks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Identify an existing table alias that is compatible with the current query clause.
|
||||
*
|
||||
* We avoid unnecessary table joins by allowing each clause to look for
|
||||
* an existing table alias that is compatible with the query that it
|
||||
* needs to perform. An existing alias is compatible if (a) it is a
|
||||
* sibling of $clause (ie, it's under the scope of the same relation),
|
||||
* and (b) the combination of operator and relation between the clauses
|
||||
* allows for a shared table join. In the case of WP_Meta_Query, this
|
||||
* only applies to IN clauses that are connected by the relation OR.
|
||||
*
|
||||
* @since 4.1.0
|
||||
* @access protected
|
||||
*
|
||||
* @param array $clause Query clause.
|
||||
* @param array $parent_query Parent query of $clause.
|
||||
* @return string|bool Table alias if found, otherwise false.
|
||||
*/
|
||||
protected function find_compatible_table_alias( $clause, $parent_query ) {
|
||||
$alias = false;
|
||||
|
||||
foreach ( $parent_query as $sibling ) {
|
||||
// If the sibling has no alias yet, there's nothing to check.
|
||||
if ( empty( $sibling['alias'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We're only interested in siblings that are first-order clauses.
|
||||
if ( ! is_array( $sibling ) || ! $this->is_first_order_clause( $sibling ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$compatible_compares = array();
|
||||
|
||||
// Clauses connected by OR can share joins as long as they have "positive" operators.
|
||||
if ( 'OR' === $parent_query['relation'] ) {
|
||||
$compatible_compares = array( '=', 'IN', 'BETWEEN', 'LIKE', 'REGEXP', 'RLIKE', '>', '>=', '<', '<=' );
|
||||
|
||||
// Clauses joined by AND with "negative" operators share a join only if they also share a key.
|
||||
} else if ( isset( $sibling['key'] ) && isset( $clause['key'] ) && $sibling['key'] === $clause['key'] ) {
|
||||
$compatible_compares = array( '!=', 'NOT IN', 'NOT LIKE' );
|
||||
}
|
||||
|
||||
$clause_compare = strtoupper( $clause['compare'] );
|
||||
$sibling_compare = strtoupper( $sibling['compare'] );
|
||||
if ( in_array( $clause_compare, $compatible_compares ) && in_array( $sibling_compare, $compatible_compares ) ) {
|
||||
$alias = $sibling['alias'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return apply_filters( 'meta_query_find_compatible_table_alias', $alias, $clause, $parent_query, $this ) ;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -73,7 +73,7 @@ class Tests_Meta_Query extends WP_UnitTestCase {
|
||||
|
||||
$sql = $query->get_sql( 'post', $wpdb->posts, 'ID' );
|
||||
|
||||
$this->assertEquals( 2, substr_count( $sql['join'], 'INNER JOIN' ) );
|
||||
$this->assertEquals( 1, substr_count( $sql['join'], 'INNER JOIN' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -476,12 +476,7 @@ class Tests_Meta_Query extends WP_UnitTestCase {
|
||||
|
||||
$sql = $query->get_sql( 'post', $wpdb->posts, 'ID', $this );
|
||||
|
||||
// We should have 2 joins - one for my_first_key and one for my_second_key
|
||||
$this->assertEquals( 2, substr_count( $sql['join'], 'JOIN' ) );
|
||||
|
||||
// The WHERE should check my_third_key against an unaliased table
|
||||
$this->assertEquals( 1, substr_count( $sql['where'], "$wpdb->postmeta.meta_key = 'my_third_key'" ) );
|
||||
|
||||
$this->assertEquals( 3, substr_count( $sql['join'], 'JOIN' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -546,9 +541,6 @@ class Tests_Meta_Query extends WP_UnitTestCase {
|
||||
// NOT EXISTS compare queries are not key-only so should not be non-aliased
|
||||
$this->assertSame( 0, substr_count( $sql['where'], "$wpdb->postmeta.meta_key = 'baz'" ) );
|
||||
|
||||
// When a value exists, it's not a key-only query
|
||||
$this->assertSame( 0, substr_count( $sql['where'], "$wpdb->postmeta.meta_key = 'barry'" ) );
|
||||
|
||||
// 'AND' queries don't have key-only queries
|
||||
$query2 = new WP_Meta_Query( array(
|
||||
'relation' => 'AND',
|
||||
|
@ -646,6 +646,385 @@ class Tests_Post_Query extends WP_UnitTestCase {
|
||||
$this->assertEqualSets( $expected, $query->posts );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 24093
|
||||
*/
|
||||
public function test_meta_query_relation_or_compare_equals() {
|
||||
$posts = $this->factory->post->create_many( 4 );
|
||||
add_post_meta( $posts[0], 'color', 'orange' );
|
||||
add_post_meta( $posts[1], 'color', 'blue' );
|
||||
add_post_meta( $posts[1], 'vegetable', 'onion' );
|
||||
add_post_meta( $posts[2], 'vegetable', 'shallot' );
|
||||
|
||||
$query = new WP_Query( array(
|
||||
'meta_query' => array(
|
||||
'relation' => 'OR',
|
||||
array(
|
||||
'key' => 'vegetable',
|
||||
'value' => 'onion',
|
||||
'compare' => '=',
|
||||
),
|
||||
array(
|
||||
'key' => 'vegetable',
|
||||
'value' => 'shallot',
|
||||
'compare' => '=',
|
||||
),
|
||||
),
|
||||
'update_post_meta_cache' => false,
|
||||
'update_post_term_cache' => false,
|
||||
'fields' => 'ids',
|
||||
) );
|
||||
|
||||
$expected = array( $posts[1], $posts[2] );
|
||||
$this->assertEqualSets( $expected, $query->posts );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 24093
|
||||
*/
|
||||
public function test_meta_query_relation_or_compare_equals_different_keys() {
|
||||
$posts = $this->factory->post->create_many( 4 );
|
||||
add_post_meta( $posts[0], 'color', 'orange' );
|
||||
add_post_meta( $posts[1], 'color', 'blue' );
|
||||
add_post_meta( $posts[1], 'vegetable', 'onion' );
|
||||
add_post_meta( $posts[2], 'vegetable', 'shallot' );
|
||||
|
||||
$query = new WP_Query( array(
|
||||
'meta_query' => array(
|
||||
'relation' => 'OR',
|
||||
array(
|
||||
'key' => 'vegetable',
|
||||
'value' => 'onion',
|
||||
'compare' => '=',
|
||||
),
|
||||
array(
|
||||
'key' => 'color',
|
||||
'value' => 'orange',
|
||||
'compare' => '=',
|
||||
),
|
||||
),
|
||||
'update_post_meta_cache' => false,
|
||||
'update_post_term_cache' => false,
|
||||
'fields' => 'ids',
|
||||
) );
|
||||
|
||||
$expected = array( $posts[0], $posts[1] );
|
||||
$this->assertEqualSets( $expected, $query->posts );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 24093
|
||||
*/
|
||||
public function test_meta_query_relation_or_compare_equals_and_in() {
|
||||
$posts = $this->factory->post->create_many( 4 );
|
||||
add_post_meta( $posts[0], 'color', 'orange' );
|
||||
add_post_meta( $posts[1], 'color', 'blue' );
|
||||
add_post_meta( $posts[1], 'vegetable', 'onion' );
|
||||
add_post_meta( $posts[2], 'vegetable', 'shallot' );
|
||||
|
||||
$query = new WP_Query( array(
|
||||
'meta_query' => array(
|
||||
'relation' => 'OR',
|
||||
array(
|
||||
'key' => 'vegetable',
|
||||
'value' => 'onion',
|
||||
'compare' => '=',
|
||||
),
|
||||
array(
|
||||
'key' => 'color',
|
||||
'value' => array( 'orange', 'green' ),
|
||||
'compare' => 'IN',
|
||||
),
|
||||
),
|
||||
'update_post_meta_cache' => false,
|
||||
'update_post_term_cache' => false,
|
||||
'fields' => 'ids',
|
||||
) );
|
||||
|
||||
$expected = array( $posts[0], $posts[1] );
|
||||
$this->assertEqualSets( $expected, $query->posts );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 24093
|
||||
*/
|
||||
public function test_meta_query_relation_or_compare_equals_and_like() {
|
||||
$posts = $this->factory->post->create_many( 4 );
|
||||
add_post_meta( $posts[0], 'color', 'orange' );
|
||||
add_post_meta( $posts[1], 'color', 'blue' );
|
||||
add_post_meta( $posts[1], 'vegetable', 'onion' );
|
||||
add_post_meta( $posts[2], 'vegetable', 'shallot' );
|
||||
|
||||
$query = new WP_Query( array(
|
||||
'meta_query' => array(
|
||||
'relation' => 'OR',
|
||||
array(
|
||||
'key' => 'vegetable',
|
||||
'value' => 'onion',
|
||||
'compare' => '=',
|
||||
),
|
||||
array(
|
||||
'key' => 'vegetable',
|
||||
'value' => 'hall',
|
||||
'compare' => 'LIKE',
|
||||
),
|
||||
),
|
||||
'update_post_meta_cache' => false,
|
||||
'update_post_term_cache' => false,
|
||||
'fields' => 'ids',
|
||||
) );
|
||||
|
||||
$expected = array( $posts[1], $posts[2] );
|
||||
$this->assertEqualSets( $expected, $query->posts );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 24093
|
||||
*/
|
||||
public function test_meta_query_relation_or_compare_equals_and_between() {
|
||||
$posts = $this->factory->post->create_many( 4 );
|
||||
add_post_meta( $posts[0], 'number_of_colors', '2' );
|
||||
add_post_meta( $posts[1], 'number_of_colors', '5' );
|
||||
add_post_meta( $posts[1], 'vegetable', 'onion' );
|
||||
add_post_meta( $posts[2], 'vegetable', 'shallot' );
|
||||
|
||||
$query = new WP_Query( array(
|
||||
'meta_query' => array(
|
||||
'relation' => 'OR',
|
||||
array(
|
||||
'key' => 'vegetable',
|
||||
'value' => 'shallot',
|
||||
'compare' => '=',
|
||||
),
|
||||
array(
|
||||
'key' => 'number_of_colors',
|
||||
'value' => array( 1, 3 ),
|
||||
'compare' => 'BETWEEN',
|
||||
'type' => 'SIGNED',
|
||||
),
|
||||
),
|
||||
'update_post_meta_cache' => false,
|
||||
'update_post_term_cache' => false,
|
||||
'fields' => 'ids',
|
||||
) );
|
||||
|
||||
$expected = array( $posts[0], $posts[2] );
|
||||
$this->assertEqualSets( $expected, $query->posts );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 24093
|
||||
*/
|
||||
public function test_meta_query_relation_and_compare_in_same_keys() {
|
||||
$posts = $this->factory->post->create_many( 4 );
|
||||
add_post_meta( $posts[0], 'color', 'orange' );
|
||||
add_post_meta( $posts[1], 'color', 'blue' );
|
||||
add_post_meta( $posts[1], 'vegetable', 'onion' );
|
||||
add_post_meta( $posts[2], 'vegetable', 'shallot' );
|
||||
add_post_meta( $posts[3], 'vegetable', 'banana' );
|
||||
add_post_meta( $posts[3], 'vegetable', 'onion' );
|
||||
|
||||
$query = new WP_Query( array(
|
||||
'meta_query' => array(
|
||||
'relation' => 'AND',
|
||||
array(
|
||||
'key' => 'vegetable',
|
||||
'value' => array( 'onion', 'shallot' ),
|
||||
'compare' => 'IN',
|
||||
),
|
||||
array(
|
||||
'key' => 'vegetable',
|
||||
'value' => array( 'banana' ),
|
||||
'compare' => 'IN',
|
||||
),
|
||||
),
|
||||
'update_post_meta_cache' => false,
|
||||
'update_post_term_cache' => false,
|
||||
'fields' => 'ids',
|
||||
) );
|
||||
|
||||
$expected = array( $posts[3] );
|
||||
$this->assertEqualSets( $expected, $query->posts );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 24093
|
||||
*/
|
||||
public function test_meta_query_relation_and_compare_in_different_keys() {
|
||||
$posts = $this->factory->post->create_many( 4 );
|
||||
add_post_meta( $posts[0], 'color', 'orange' );
|
||||
add_post_meta( $posts[1], 'color', 'blue' );
|
||||
add_post_meta( $posts[1], 'vegetable', 'onion' );
|
||||
add_post_meta( $posts[1], 'vegetable', 'shallot' );
|
||||
add_post_meta( $posts[2], 'vegetable', 'shallot' );
|
||||
add_post_meta( $posts[3], 'vegetable', 'banana' );
|
||||
|
||||
$query = new WP_Query( array(
|
||||
'meta_query' => array(
|
||||
'relation' => 'AND',
|
||||
array(
|
||||
'key' => 'vegetable',
|
||||
'value' => array( 'onion', 'shallot' ),
|
||||
'compare' => 'IN',
|
||||
),
|
||||
array(
|
||||
'key' => 'color',
|
||||
'value' => array( 'blue' ),
|
||||
'compare' => 'IN',
|
||||
),
|
||||
),
|
||||
'update_post_meta_cache' => false,
|
||||
'update_post_term_cache' => false,
|
||||
'fields' => 'ids',
|
||||
) );
|
||||
|
||||
$expected = array( $posts[1] );
|
||||
$this->assertEqualSets( $expected, $query->posts );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 24093
|
||||
*/
|
||||
public function test_meta_query_relation_and_compare_not_equals() {
|
||||
$posts = $this->factory->post->create_many( 4 );
|
||||
add_post_meta( $posts[0], 'color', 'orange' );
|
||||
add_post_meta( $posts[1], 'color', 'blue' );
|
||||
add_post_meta( $posts[1], 'vegetable', 'onion' );
|
||||
add_post_meta( $posts[2], 'vegetable', 'shallot' );
|
||||
add_post_meta( $posts[3], 'vegetable', 'banana' );
|
||||
|
||||
$query = new WP_Query( array(
|
||||
'meta_query' => array(
|
||||
'relation' => 'AND',
|
||||
array(
|
||||
'key' => 'vegetable',
|
||||
'value' => 'onion',
|
||||
'compare' => '!=',
|
||||
),
|
||||
array(
|
||||
'key' => 'vegetable',
|
||||
'value' => 'shallot',
|
||||
'compare' => '!=',
|
||||
),
|
||||
),
|
||||
'update_post_meta_cache' => false,
|
||||
'update_post_term_cache' => false,
|
||||
'fields' => 'ids',
|
||||
) );
|
||||
|
||||
$expected = array( $posts[3] );
|
||||
$this->assertEqualSets( $expected, $query->posts );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 24093
|
||||
*/
|
||||
public function test_meta_query_relation_and_compare_not_equals_different_keys() {
|
||||
$posts = $this->factory->post->create_many( 4 );
|
||||
|
||||
// !shallot, but orange.
|
||||
add_post_meta( $posts[0], 'color', 'orange' );
|
||||
add_post_meta( $posts[0], 'vegetable', 'onion' );
|
||||
|
||||
// !orange, but shallot.
|
||||
add_post_meta( $posts[1], 'color', 'blue' );
|
||||
add_post_meta( $posts[1], 'vegetable', 'shallot' );
|
||||
|
||||
// Neither.
|
||||
add_post_meta( $posts[2], 'color', 'blue' );
|
||||
add_post_meta( $posts[2], 'vegetable', 'onion' );
|
||||
|
||||
$query = new WP_Query( array(
|
||||
'meta_query' => array(
|
||||
'relation' => 'AND',
|
||||
array(
|
||||
'key' => 'vegetable',
|
||||
'value' => 'shallot',
|
||||
'compare' => '!=',
|
||||
),
|
||||
array(
|
||||
'key' => 'color',
|
||||
'value' => 'orange',
|
||||
'compare' => '!=',
|
||||
),
|
||||
),
|
||||
'update_post_meta_cache' => false,
|
||||
'update_post_term_cache' => false,
|
||||
'fields' => 'ids',
|
||||
) );
|
||||
|
||||
$expected = array( $posts[2] );
|
||||
$this->assertEqualSets( $expected, $query->posts );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 24093
|
||||
*/
|
||||
public function test_meta_query_relation_and_compare_not_equals_not_in() {
|
||||
$posts = $this->factory->post->create_many( 4 );
|
||||
add_post_meta( $posts[0], 'color', 'orange' );
|
||||
add_post_meta( $posts[1], 'color', 'blue' );
|
||||
add_post_meta( $posts[1], 'vegetable', 'onion' );
|
||||
add_post_meta( $posts[2], 'vegetable', 'shallot' );
|
||||
add_post_meta( $posts[3], 'vegetable', 'banana' );
|
||||
|
||||
$query = new WP_Query( array(
|
||||
'meta_query' => array(
|
||||
'relation' => 'AND',
|
||||
array(
|
||||
'key' => 'vegetable',
|
||||
'value' => 'onion',
|
||||
'compare' => '!=',
|
||||
),
|
||||
array(
|
||||
'key' => 'vegetable',
|
||||
'value' => array( 'shallot' ),
|
||||
'compare' => 'NOT IN',
|
||||
),
|
||||
),
|
||||
'update_post_meta_cache' => false,
|
||||
'update_post_term_cache' => false,
|
||||
'fields' => 'ids',
|
||||
) );
|
||||
|
||||
$expected = array( $posts[3] );
|
||||
$this->assertEqualSets( $expected, $query->posts );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 24093
|
||||
*/
|
||||
public function test_meta_query_relation_and_compare_not_equals_and_not_like() {
|
||||
$posts = $this->factory->post->create_many( 4 );
|
||||
add_post_meta( $posts[0], 'color', 'orange' );
|
||||
add_post_meta( $posts[1], 'color', 'blue' );
|
||||
add_post_meta( $posts[1], 'vegetable', 'onion' );
|
||||
add_post_meta( $posts[2], 'vegetable', 'shallot' );
|
||||
add_post_meta( $posts[3], 'vegetable', 'banana' );
|
||||
|
||||
$query = new WP_Query( array(
|
||||
'meta_query' => array(
|
||||
'relation' => 'AND',
|
||||
array(
|
||||
'key' => 'vegetable',
|
||||
'value' => 'onion',
|
||||
'compare' => '!=',
|
||||
),
|
||||
array(
|
||||
'key' => 'vegetable',
|
||||
'value' => 'hall',
|
||||
'compare' => 'NOT LIKE',
|
||||
),
|
||||
),
|
||||
'update_post_meta_cache' => false,
|
||||
'update_post_term_cache' => false,
|
||||
'fields' => 'ids',
|
||||
) );
|
||||
|
||||
$expected = array( $posts[3] );
|
||||
$this->assertEqualSets( $expected, $query->posts );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 23033
|
||||
* @group meta
|
||||
@ -1040,13 +1419,13 @@ class Tests_Post_Query extends WP_UnitTestCase {
|
||||
$post_id6 = $this->factory->post->create();
|
||||
add_post_meta( $post_id6, 'baz', 0 );
|
||||
|
||||
$posts = get_posts( array( 'meta_key' => 'foo', 'meta_value' => '0' ) );
|
||||
$this->assertEquals( 1, count ( $posts ) );
|
||||
foreach ( $posts as $post ) {
|
||||
$q = new WP_Query( array( 'meta_key' => 'foo', 'meta_value' => '0' ) );
|
||||
$this->assertEquals( 1, count ( $q->posts ) );
|
||||
foreach ( $q->posts as $post ) {
|
||||
$this->assertInstanceOf( 'WP_Post', $post );
|
||||
$this->assertEquals( 'raw', $post->filter );
|
||||
}
|
||||
$this->assertEquals( $post_id, $posts[0]->ID );
|
||||
$this->assertEquals( $post_id, $q->posts[0]->ID );
|
||||
|
||||
$posts = get_posts( array( 'meta_key' => 'bar', 'meta_value' => '0' ) );
|
||||
$this->assertEquals( 2, count ( $posts ) );
|
||||
|
Loading…
x
Reference in New Issue
Block a user