I have a large WP site with about 2k posts and 150k postmeta. When browsing to a large category, the archive loads very slow due to the WP query that is seeking for featured posts, to show them first.
I know that the ultimate target is to optimize the postmeta table, but for now I would like to know if there is any possible way to optimize the meta query (one at the bottom) below.
I did some debugging (see the //comments) and found that the 'compare' => '!=' is causing for the query to get really slow. I've also found that '' or '<' is speeding up the query a lot, with the same results, but I am not sure if that is a proper way to go. Please let me know if there is a way to speed up this query. Thanks
function appthemes_addon_on_top_query( $wp_query ){
$addon_type = $wp_query->get( 'addon_on_top' );
if( ! $addon_type || ! appthemes_addon_exists( $addon_type ) ) {
return;
}
$addon_info = appthemes_get_addon_info( $addon_type );
$flag_key = $addon_info['flag_key']; //custom field for featured posts
$meta_query = (array) $wp_query->get( 'meta_value', 1 );
$meta_query = array_filter( $meta_query );
$meta_query[] = array(
'relation' => 'OR',
array(
'key' => $flag_key,
'compare' => 'NOT EXISTS',
),
array(
'relation' => 'OR',
//Deleting the array below makes the query quick, but i need it to show featured posts first
array(
'key' => $flag_key,
'value' => 1,
),
//Deleting the array below makes the query quick, but i need it to properly sorting the posts by date (see below: $wp_query->set( 'orderby')
array(
'key' => $flag_key,
'value' => 1,
'compare' => '!=', //It is about this comparison. '' or '<' makes the query quick, but i am not sure if that is a proper replacement. Also not sure why '!=' makes it this slow.
),
),
);
$wp_query->set( 'meta_query', $meta_query );
$wp_query->set( 'orderby', array( 'meta_value' => 'DESC', 'date' => 'DESC' ) );
}
Ok, Ive found that 'compare' => '!=' slows it down, but 'compare' => '<>' does not. I guess both are the same, so I guess this question is solved.
Related
I'm trying to figure out how to achieve a specific query to modify my search results for posts with WordPress. I'm trying to search via a custom field called "Common Authors".
There can be multiple Common authors, which is causing my query to sometimes fail. Here is what I've got for now:
<?php
...
$query->set('meta_key', 'common_authors');
$query->set('meta_value', serialize( array(strval($_GET['common_author'])))); // I get a single ID from the url as a string
$query->set('meta_compare', 'IN');
This is what I see when I var_dump the query:
'meta_key' => string 'common_authors'
'meta_value' => string 'a:1:{i:0;s:5:"17145";}'
This works fine if there is only one common_author.
However, there can be multiple common_authors for a post. Here is a meta_value example from the database:
a:4:{i:0;s:5:"14409";i:1;s:5:"17145";i:2;s:5:"14407";i:3;s:5:"14406";}
Could somebody help me out, to figure out how to adapt my query, so that it would return this one as well?
Try This One Work Prefect!
$args = array(
'post_type' => 'post',
'meta_query' => array(
array(
'key' => 'common_authors',
'value' => array ( 'author1', 'author2', 'author3' ),
'compare' => 'IN'
)
)
);
$query = new WP_QUERY($args);
if You Need To Use It In pre_get_posts
$meta_query = array(
array(
'key' => 'common_authors',
'value' => array ( 'author1', 'author2', 'author3' ),
'compare' => 'IN'
)
);
$query->set('meta_query', $meta_query);
I've tried to implement this, while trying to apply some bits of code taken from here and there.
I'm using Elementor Page Builder in a website and I created a custom post type with ACF. Inside that post type, there's a field for a start_date.
The idea is to only show posts in homepage when the date is equal or higher to present date.
Right now the code is like this:
add_action( 'elementor/query/por_data', function( $query ) {
// Here we set the query to display posts
// after specific date
$query->set( 'date_query', array(
array(
'after' => 'May 17, 2020',
)
) );
} );
This obviously just shows posts created after 17 May 2020, which isn't what I want. The idea is to grab the date from that ACF field, compare it with current date and show results accordingly. The custom field type name is "curso"
Found this piece of code but can seem to merge the two together as I have very to little knowledge in programming (yet)
$args = array(
'post_type' => 'events',
'orderby' => 'event_date',
'meta_key' => 'event_date',
'order' => 'ASC',
'meta_query' => array(
array(
'key' => 'event_date',
'value' => date('Y-m-d',time()),
'compare' => '>'
)
),
);
$q = new WP_Query($args);
var_dump($q->request);
Can someone help? Thanks
I tried to explain it in the comments - hopefully you will get a better understanding of it now.
add_action( 'elementor/query/por_data', function( $query ) {
$meta_query = $query->get( 'meta_query' );
// Append our meta query instead of overwriting all elementors own metaqueries
if($meta_query == ""){
$meta_query = array();
}
$meta_query[] = array(
array(
'key' => 'start_date', //Or whatever you field is called
'value' => date('Y-m-d',time()), //Make sure that the format is correct here
'compare' => '>' //Means that the value should be higher than the key, so this is what you want - If you also need the current dates posts, then you need to use '>=' instead
)
);
$query->set( 'meta_query', $meta_query ); //since we appended our query, you can safely set the meta querie now.
} );
You might need to tweak it a little, but this will lead you the right way.
I have been through several topics on sorting taxonomies and custom fields, including some that were promising. However, I am still not constructing my query args correctly it seems.
I am "fixing" someone else's code in a custom theme that is used in a multisite configuration, but I am able to override/filter the query on a per-site basis.
The query is called via the theme with the following:
return apply_filters( 'theme_query_args_filter', $args);
I have custom fields in the "role" taxonomy that include:
last_name
first_name
Pretty common I think.
However, when the args are executed, the following filter args are sorting only by last name (key part is the else clause):
function my_args_filter( $args ) {
if (is_home() && !is_search()) {
$args['meta_key'] = 'event_date';
$args['orderby'] = 'event_date';
$args['order'] = 'DESC';
}
else {
$tax = $args['taxonomy'];
$theterm = $args['term'];
$args = array (
'taxonomy' => $tax,
'term' => $theterm,
'meta_key' => 'last_name',
'orderby' => 'meta_value',
'order' => 'ASC'
);
}
add_filter( 'theme_query_args_filter', 'my_args_filter' );
I've tried to modify the orderby as indicated in https://make.wordpress.org/core/2014/08/29/a-more-powerful-order-by-in-wordpress-4-0/ to use an array to do a multisort, but I'm hitting my head up against the wall. I think the problem is that the code is written using a mixture of old ways of doing things and new ways.
Any advice is appreciated. According to the example on in the docs above, I SHOULD be able to pass in multiple meta key/value/orders via an array, but I'm just not getting it.
Thanks for any leads you might have. (long-time listener, first-time caller)
(I also looked at https://wordpress.stackexchange.com/questions/109849/order-by-desc-asc-in-custom-wp-query but I couldn't extrapolate that example to this one either)
Figured it out on my most recent try. Hat tip to this solution https://wordpress.stackexchange.com/questions/249881/multiple-custom-fields-for-orderby-in-wp-query by Nath. if there is a more elegant way to do it, please let me know.
$tax = $args['taxonomy'];
$theterm = $args['term'];
$paged = $args['paged'];
$args = array (
'taxonomy' => $tax,
'term' => $theterm,
'paged' => $paged,
'posts_per_page' => '10',
'meta_query' => array(
array(
'relation' => 'AND',
'last_name' =>
array(
'key' => 'last_name',
'compare' => 'EXISTS',
),
'first_name' =>
array(
'key' => 'first_name',
'compare' => 'EXISTS',
),
),
),
'orderby' => array (
'last_name' => 'ASC',
'first_name' => 'ASC',
)
);
}
Is there a way to have a meta_query with sub relations? I don't want one relation to apply to all of the query's arrays.
I tried this, but to no avail:
'meta_query' => array(
array(
'key' => 'event_type',
'value' => 'session',
'compare' => 'LIKE'
),
array(
'relation' => 'OR',
array(
'key' => 'event_video',
'value' => '0',
'compare' => '>'
),
array(
'key' => 'event_summary',
'value' => '',
'compare' => '!='
)
),
array(
'key' => 'event_date_single',
'value' => '',
'compare' => 'LIKE'
),
array(
'key' => 'event_start_time',
'value' => '',
'compare' => 'LIKE'
)
)
So 'relation' => 'OR' should only apply between event_video and event_summary. Everything else is independent of one another. Is this possible somehow?
Thanks
It looks like this could be a meta_query limitation. WP Codex shows that you can use relations for meta queries, but as far as I see the limitation is that you can only use one kind of relation for the whole meta_query.
This means you can have a full OR or a full AND (default) relation between meta_query parameters, but you can never have them both combined, which is just what you want.
As I was not glad when I faced it at WP Codex, I went deeper in the matter by manually searching the code. So I found out that WP_Query calls WP_Meta_Query to parse meta_query parameters. This is WP_Meta_Query's class constructor (you can find it in wp-includes/meta.php at line 643 in WP 3.6):
/**
* Constructor
*
* #param array $meta_query (optional) A meta query
*/
function __construct( $meta_query = false ) {
if ( !$meta_query )
return;
if ( isset( $meta_query['relation'] ) && strtoupper( $meta_query['relation'] ) == 'OR' ) {
$this->relation = 'OR';
} else {
$this->relation = 'AND';
}
$this->queries = array();
foreach ( $meta_query as $key => $query ) {
if ( ! is_array( $query ) )
continue;
$this->queries[] = $query;
}
}
Long story short, the second if shows that this class will work only either with a OR or AND relation, but never with both of them combined.
So, unfortunately, it seems that it's not possible to achieve what you are trying to do (at least until WP 3.6). I guess your best shot is to split the main query, first doing specific Meta Queries and then applying their results(returned metas) to your main query.
I know that this is not a solution but an answer. Nevertheless, I hope this may help you to find a solution for this matter.
Consider a situation, I've post meta fields:
Min_Price
Max_Price
So every post has these two fields, and value can be any integer from non zero to 1000.
Lets take an example:
post #1 min_price = 5; max_price = 100
post #2 min_price = 50; max_price = 150
I'd like to make custom order logics to use with query_posts. So users can select price ranges and see posts with selected values.
Now if I had only 1 price and 1 field, it would be easy to let users select price range and get posts which fall into these range, like so:
query_posts( array(
'post_type' => 'product',
'meta_query' => array(
array(
'key' => 'price',
'value' => array( 100, 200 ),
'compare' => 'BETWEEN',
'type' => 'numeric',
)
)
) );
So here every post with price between 100-200 would be visible.
But I have a range of values... and I thought this would work:
query_posts( array(
'post_type' => 'product',
'meta_query' => array(
array(
'key' => 'min_price',
'value' => array( 100, 200 ),
'compare' => 'BETWEEN',
'type' => 'numeric',
),
array(
'key' => 'max_price',
'value' => array( 100, 200 ),
'compare' => 'BETWEEN',
'type' => 'numeric',
)
)
) );
Here client selected range 100<->200
Both post #1 and post #2 have values that fall in this range, so visitor will be interested in both of them. Unfortunately my query logic excludes them.
My brain is not working well right now, so I'm missing something simple. Any hints?
I now know your way should work, except McNab is probably right (there are plenty of examples right here: http://codex.wordpress.org/Class_Reference/WP_Query#Custom_Field_Parameters) but I'll let my old answer remain. Someone might find it useful for non-standard stuff.
I don't know if you can do this using query post parameters, but I looked it up in the source code and this is how the query looks:
SELECT $found_rows $distinct $fields
FROM $wpdb->posts $join
WHERE 1=1 $where
$groupby
$orderby
$limits
And these are filters corresponding to the relevant query variables:
$where = apply_filters_ref_array('posts_where', array( $where, &$this ) );
$join = apply_filters_ref_array('posts_join', array( $join, &$this ) );
So that means that the problem can be reduced to SQL:
function postmeta_join( $join )
{
$join .= "
LEFT JOIN $wpdb->postmeta AS max_price ON max_price.meta_key = 'max_price'
LEFT JOIN $wpdb->postmeta AS min_price ON min_price.meta_key = 'min_price'";
return $join;
}
function filter_prices($where)
{
$where .= " AND max_price.max_price BETWEEN 100, 200 AND min_price.min_price BETWEN 100, 200";
return $where;
}
add_filter( 'posts_where', 'filter_prices' );
add_filter( 'posts_join', 'postmeta_join' );
Now I'm not an SQL expert so there may be problems with this. But if you don't get any other answer, you could play with this. (And BTW, you probably want to remove the filters when you're done with them.)
What you've done should work but you've missed the 'relation' parameter. It should be;
'meta_query' => array(
'relation' => 'AND',
array(
'key' => 'min_price',
'value' => array( 100, 200 ),
'compare' => 'BETWEEN',
'type' => 'numeric',
),
array(
'key' => 'max_price',
'value' => array( 100, 200 ),
'compare' => 'BETWEEN',
'type' => 'numeric',
)
)
The other thing to note is that in the first instance the meta_id is 'price' and in the second you are using 'min_price' and 'max_price'. Have these definitely been set?
I'm mentioning this because recently I had a hard time on these meta_queries with multiple sub-arrays and the AND relation where the values had been submitted by the user in a form (exactly like my understanding of your question). There was an issue where the query didn't work where there are null values for items in the array. This is meant to be fixed by scribu;
http://core.trac.wordpress.org/ticket/18158
But I could not get this to work at all. Tearing my hair out. I ended up checking the submitted value to see if there was one and then creating the arrays individually. The query would work if either $min_array or $max_array was NULL, but not at all if either of the meta_values was NULL and it was built the way you are doing it. I hope this makes sense, my code is below;
if ($min_price) {
$min_array = array('key' => 'min_price','value' => $min_price, 'value' => '=', 'type' => 'numeric');
}
if ($max_price) {
$max_array = array('key' => 'max_price','value' => $max_price, 'value' => '=', 'type' => 'numeric');
}
'meta_query' => array(
'relation' => 'AND',
$min_array,
$max_array
)