i am trying to fetch data form database for my custom plugin and post type. my query arguments should be like
$args = array(
'post_type' => 'products',
'post_status'=> 'publish',
'meta_query' => array(
'relation' => 'OR',
array( 'key'=>'product_commercial',
'value'=>'on',
'compare'=>'='
),
array( 'key'=>'product_exterior',
'value'=>'on',
'compare'=>'='
)
)
);
$search_query = new WP_Query( $args );
But i am trying to add meta key values dynamically like :
$inner_arrays=array();
$count = 0;
foreach($values as $value){
if($value){
$inner_arrays[$count]['key'] .= $value;
$inner_arrays[$count]['value'] .= 'on';
$inner_arrays[$count]['compare'] .= '=';
$count++;
}
}
$args = array(
'post_type' => 'products',
'post_status'=> 'publish',
'meta_query' => array(
'relation' => 'OR',
$inner_arrays
)
);
//values are some random values (say fetched from db).
Now when i print the query using echo "<pre>Last SQL-Query: {$search_query->request}".'<br/>';
it displays
Last SQL-Query: SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts INNER JOIN wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id ) INNER JOIN wp_postmeta AS mt1 ON ( wp_posts.ID = mt1.post_id ) INNER JOIN wp_postmeta AS mt2 ON ( wp_posts.ID = mt2.post_id ) WHERE 1=1 AND (
(
( wp_postmeta.meta_key = 'product_commercial' AND CAST(wp_postmeta.meta_value AS CHAR) = 'on' )
**AND**
( mt1.meta_key = 'product_framed' AND CAST(mt1.meta_value AS CHAR) = 'on' )
**AND**
( mt2.meta_key = 'product_horizontal' AND CAST(mt2.meta_value AS CHAR) = 'on' )
)
) AND wp_posts.post_type = 'products' AND ((wp_posts.post_status = 'publish')) GROUP BY wp_posts.ID ORDER BY wp_posts.post_date DESC LIMIT 0, 10
PROBLEM: i am using " relation => OR ", but getting "AND" in sql query. Where i am doing wrong?
$args['meta_query'] is assigned the following:
array(
'relation' => 'OR',
$inner_arrays
)
If you were to inspect this, e.g. by doing print_r( $args['meta_query'] );, you'd see:
Array
(
[relation] => OR
[0] => Array
(
[0] => Array
(
key => product_commercial,
value => on,
compare => =
),
[1] => Array
(
key => product_exterior,
value => on,
compare => =
)
)
)
In other words, the $inner_arrays array has itself become a sub-array of $args['meta_query']. As documented under Custom Field Parameters:
Important Note: meta_query takes an array of meta query arguments arrays (it takes an array of arrays) - you can see this in the examples below. This construct allows you to query multiple metadatas by using the relation parameter in the first (outer) array to describe the boolean relationship between the meta queries. Accepted arguments are 'AND', 'OR'. The default is 'AND'.
Consequently you are combining your elements of $inner_arrays using the default relation of AND, whilst then combining the result of that with any other members of $args['meta_query'] (there aren't any) using the explicit relation of OR.
You must instead either append each element of $inner_arrays to meta_query, or else just set $inner_arrays['relation'] = 'OR'.
Related
In a client website that should display upcoming territory events I got a plugin (called EventsManager), that has been modified to fit our needs.
That plugin, by the way, got an huge query searching for the current events by two parameters, date_start and date_end.
This query affect only one page, and only one function.
$args_filter = $tax_query = array();
$paged = '';
$args_basic = $this->ovaem_query_base($paged, $orderby, $order, $show_past);
switch ($filter) {
[...]
case 'upcomming_showing':
$args_filter = array(
'posts_per_page' => $count,
'meta_query' => array(
array(
'relation' => 'OR',
array(
'key' => self::$prefix . '_date_start_time',
'value' => current_time('timestamp'),
'compare' => '>',
),
array(
'relation' => 'AND',
array(
'key' => self::$prefix . '_date_start_time',
'value' => current_time('timestamp'),
'compare' => '<',
),
array(
'key' => self::$prefix . '_date_end_time',
'value' => current_time('timestamp'),
'compare' => '>=',
),
),
),
),
);
break;
[...]
$args = array_merge_recursive($args_basic, $args_filter, $tax_query);
$eventlist = new WP_Query($args);
return $eventlist;
The resultant query, analized with QueryMonitor is:
SELECT SQL_CALC_FOUND_ROWS wpzt_posts.ID
FROM wpzt_posts
INNER JOIN wpzt_postmeta
ON ( wpzt_posts.ID = wpzt_postmeta.post_id )
INNER JOIN wpzt_postmeta AS mt1
ON ( wpzt_posts.ID = mt1.post_id )
INNER JOIN wpzt_postmeta AS mt2
ON ( wpzt_posts.ID = mt2.post_id )
INNER JOIN wpzt_postmeta AS mt3
ON ( wpzt_posts.ID = mt3.post_id )
WHERE 1=1
AND ( wpzt_postmeta.meta_key = 'ovaem_date_start_time'
AND ( ( ( mt1.meta_key = 'ovaem_date_start_time'
AND CAST(mt1.meta_value AS SIGNED) BETWEEN '1643155199'
AND '1643241601' )
OR ( ( mt2.meta_key = 'ovaem_date_start_time'
AND mt2.meta_value < '1643155200' )
AND ( mt3.meta_key = 'ovaem_date_end_time'
AND mt3.meta_value >= '1643155200' ) ) ) ) )
AND wpzt_posts.post_type = 'event'
AND ((wpzt_posts.post_status = 'publish'))
GROUP BY wpzt_posts.ID
ORDER BY wpzt_postmeta.meta_value+0 ASC
LIMIT 0, 9
Using MySQL 5.7 and memcached, this query got 16,69 seconds to be executed.
Is there a way to optimize that?
Thanks
How the query comes out of set of code is unclear. But a direct query (inserting your parameters as applicable) could be simplified to
SELECT SQL_CALC_FOUND_ROWS
p.ID
FROM
wpzt_posts p
JOIN wpzt_postmeta mtStart
ON p.ID = mtStart.post_id
AND mtStart.meta_key = 'ovaem_date_start_time'
LEFT JOIN wpzt_postmeta mtEnd
ON p.ID = mtEnd.post_id
AND mtEnd.meta_key = 'ovaem_date_end_time'
AND mtEnd.meta_value >= '1643155200'
WHERE
p.post_type = 'event'
AND p.post_status = 'publish'
AND (
mtStart.meta_value BETWEEN '1643155199' AND '1643241601'
OR ( mtStart.meta_value < '1643155200'
AND mtEnd.meta_value > '1643155200'
)
GROUP BY
p.ID
ORDER BY
mtStart.meta_value + 0 ASC
LIMIT
0, 9
So, not knowing specifically about WordPress and how you have it configured, Does your data allow for multiple start and end time entries for a SINGLE POST ID? If so, that will result in a Cartesian Result thus over multiplying the instances.
Are you trying to count how many UNIQUE Post IDs are found, or total records found for all the meta-data entries associated with said posts.
For indexes, I would suggest having the following
Table Index on
wpzt_posts ( post_type, post_status, id )
wpzt_postmeta ( post_id, meta_key, meta_value )
I removed the CAST(meta_value AS SIGNED) since the records associated with date/time context will always be numeric, its not like you would ever encounter a time of 'this is bad data' and thus influence a bad numeric conversion. As such, the function call makes it non-sargeable and thus will not utilize an index. But if all instances of a start/end time are all digits, you wont have that confusion -- unless the physical lengths of those numeric representations actually are longer or shorter, but even in that case, a smaller number would probably indicate a date sooooo far back, it would not even be in the range being considered and thus no impact.
The second meta_query parameter is being ignored in this query.
$query = new WP_Query(array(
'post_type' => 'events',
'meta_query' => array(
'relation' => 'OR',
array(
'key' => 'event_date',
'value' => date('Ymd'),
'compare' => '>=',
),
array(
'key' => 'end_date',
'value' => date('Ymd'),
'compare' => '>=',
)
)
));
There is no mention of end_date in the resulting SQL
SELECT SQL_CALC_FOUND_ROWS inx9uju_posts.ID
FROM inx9uju_posts
INNER JOIN inx9uju_postmeta ON ( inx9uju_posts.ID = inx9uju_postmeta.post_id )
INNER JOIN inx9uju_postmeta AS mt1 ON ( inx9uju_posts.ID = mt1.post_id )
WHERE 1=1
AND ( inx9uju_postmeta.meta_key = 'event_date' AND ( mt1.meta_key = 'event_date' AND CAST(mt1.meta_value AS SIGNED) > '20180703' ) )
AND inx9uju_posts.post_type = 'events'
AND (inx9uju_posts.post_status = 'publish'
OR inx9uju_posts.post_status = 'acf-disabled'
OR inx9uju_posts.post_status = 'private')
GROUP BY inx9uju_posts.ID
ORDER BY inx9uju_postmeta.meta_value+0 ASC LIMIT 0, 30
I've spent some time checking syntax and referring to the codex but I can't see what is wrong with my code. I can't see any reason why it would be ignoring the end_date part of the meta_query parameters.
UPDATE
In this particular case I was overriding the meta_query part of the query in a separate function which was hooking into pre_get_posts. Once I updated that function, the issue was resolved.
If this is stored as an AFC, you must use the ACF syntax of Ymd (20180703).
You must also realise that WordPress can detect the BETWEEN meta_compare to detect two dates as such.
I would like to wp_query all my given post_types and apply a filter on one of the four post_types without excluding posts of the other post_types.
For example, I would like to find all posts that have P110612 in it. This code is the WooCommerce sku.
So in order for me to query on the _sku key, I apply this as a meta_query.
But, in order for me to not exclude posts without the meta key _sku, I can make an OR relation, like so:
'meta_query' => [
'relation' => 'OR',
[
'key' => '_sku',
'value' => 'P110612',
'compare' => 'LIKE',
],
[
'key' => '_sku',
'value' => 'completely',
'compare' => 'NOT EXISTS',
],
];
What this should do is show all product posts with sku P110612 and all non-product posts where P110612 is somewhere in the content. But, the result is nothing.
Below you'll find the actual query that is being executed by WordPress WP_Query.
I hope you guys can help me out!
SELECT prefix_posts.*
FROM prefix_posts
LEFT JOIN prefix_postmeta ON ( prefix_posts.ID = prefix_postmeta.post_id )
LEFT JOIN prefix_postmeta AS mt1 ON (prefix_posts.ID = mt1.post_id AND mt1.meta_key = '_sku' )
WHERE 1=1
AND prefix_posts.ID NOT IN (2,10,11,12)
AND (
(
(prefix_posts.post_title LIKE '%P110612%')
OR (prefix_posts.post_excerpt LIKE '%P110612%')
OR (prefix_posts.post_content LIKE '%P110612%')
)
)
AND (prefix_posts.post_password = '')
AND (
( prefix_postmeta.meta_key = '_sku' AND prefix_postmeta.meta_value = 'P110612' )
OR mt1.post_id IS NULL
)
AND prefix_posts.post_type IN ('page', 'newsitem', 'partner', 'product')
AND ((prefix_posts.post_status = 'publish'))
GROUP BY prefix_posts.ID
ORDER BY prefix_posts.post_title LIKE '%P110612%' DESC, prefix_posts.post_date DESC
After Googeling this for a while, I've found that this should work ref., but I don't seem to get it running.
On the backend I have a custom field on commments for setting a list of userIDs of who liked it. It is stored sanatized in the DB this way:
a:2:{i:0;s:1:"1";i:1;s:1:"2";}
which translates into
Array (
[0] => 1
[1] => 2
)
A pretty straight forward flat array where the values 1 and 2 are the user IDs of two likes. Now, I want to query comments liked by userID 1
$args = array(
'status' => 'approve',
'post_status' => 'publish',
'meta_query' => array(
array(
'key' => 'liked_by',
'value' => array(1),
'compare' => 'IN',
'type' => 'numeric'
)
)
);
// The Query
$comments_query = new WP_Comment_Query;
$comments = $comments_query->query( $args );
And I get no comments in return, why is that?
Here is the query generated:
SELECT * FROM wp_comments JOIN wp_posts ON wp_posts.ID = wp_comments.comment_post_ID INNER JOIN wp_commentmeta ON ( wp_comments.comment_ID = wp_commentmeta.comment_id ) WHERE ( comment_approved = '1' ) AND wp_posts.post_status = 'publish' AND (
( wp_commentmeta.meta_key = 'liked_by' AND CAST(wp_commentmeta.meta_value AS SIGNED) IN ('1') )
) GROUP BY wp_comments.comment_ID ORDER BY comment_date_gmt DESC
I wanted to do this over serialized data even though I cited an URL that said
don't store array of ids in one row instead loop through ids array and
normalize your likes data manually Don't serialize data into a
database field.
This is because I used ACF and added an Users field, which used serialized data. I solved my problem by avoiding ACF and just using the comment meta API.
// UserID 1 likes commentID 9
add_comment_meta(9, 'like', 1, false);
// Get comments liked by userID 1
$args = array(
'status' => 'approve',
'post_status' => 'publish',
'meta_query' => array(
array(
'key' => 'like',
'value' => 1,
'compare' => '=',
'type' => 'numeric'
)
)
);
// The Query
$comments_query = new WP_Comment_Query;
$comments = $comments_query->query( $args );
// Surprise, it's commentID 9
Notice how I use add_comment_meta with unique=false for every like instead of serializing an array into the database, making it unavailable for queries.
In wordpess 4.1 I have the following WP_Query:
$products = new WP_Query( array(
'post_type' => 'ys_product',
'orderby' => array( 'date' ),
'order' => 'DESC',
'posts_per_page' => 8,
'meta_query' => array(
'relation' => 'AND',
array(
'key' => 'ys_product_status',
'value' => 'ok'
),
array (
'key' => 'ys_product_start',
'value' => date('Ymd'),
'compare' => '>='
),
array (
'key' => 'ys_product_end',
'value' => date('Ymd'),
'compare' => '<='
)
)
) );
And on my DB I have two items with post_type 'ys_product', and with post_meta like this:
Item 1
meta_key ys_product_status = 'ok'
meta_key ys_product_start = '20141101'
meta_key ys_product_end = '20141230'
Item 2
meta_key ys_product_status = 'ok'
meta_key ys_product_start = '20141101'
meta_key ys_product_end = '20150131'
The result of date('Ymd') today is '20141226', which seems clearly between those boundaries.
But if I search only filtering by 'ys_product_status' == 'ok'; I get my two items. But when I add my two other meta, it wont return any results at all.
The contents of $products->request are:
SELECT SQL_CALC_FOUND_ROWS ysls_posts.ID
FROM ysls_posts
INNER JOIN ysls_postmeta ON ( ysls_posts.ID = ysls_postmeta.post_id )
INNER JOIN ysls_postmeta AS mt1 ON ( ysls_posts.ID = mt1.post_id )
INNER JOIN ysls_postmeta AS mt2 ON ( ysls_posts.ID = mt2.post_id )
WHERE 1=1
AND ysls_posts.post_type = 'ys_product'
AND (ysls_posts.post_status = 'publish' OR ysls_posts.post_status = 'private')
AND (
( ysls_postmeta.meta_key = 'ys_product_status' AND CAST(ysls_postmeta.meta_value AS CHAR) = 'ok' )
AND
( mt1.meta_key = 'ys_product_start' AND CAST(mt1.meta_value AS CHAR) >= '20141226' )
AND
( mt2.meta_key = 'ys_product_end' AND CAST(mt2.meta_value AS CHAR) <= '20141226' )
) GROUP BY ysls_posts.ID LIMIT 0, 8
The post_meta contents for the two posts I'm targeting:
What am I doing wrong?
array (
'key' => 'ys_product_start',
'value' => date('Ymd'),
'compare' => '>='
),
array (
'key' => 'ys_product_end',
'value' => date('Ymd'),
'compare' => '<='
)
here: ys_product_start >= date('Ymd') AND ys_product_end <= date('Ymd') its mean a row must met the both conditions but no one can
Row 1 NOT 20141101 >= 20141226 AND 20141230 <= 20141226 // false AND false = false
meta_key ys_product_start = '20141101'
meta_key ys_product_end = '20141230'
Row 2 NOT 20141101 >= 20141226 AND 20150131 <= 20141226 // false AND false = false
meta_key ys_product_start = '20141101'
meta_key ys_product_end = '20150131'