I'm trying to re-order the search results using the pre_get_posts
hook and set the orderby
parameter to relevance. I also want to put priority posts, which is based on post meta _prioritize_s
to be placed first in the results. Then all the posts after will be based on relevance as usual.
Here's the code I've been messing with:
//runs on pre_get_posts
function modify_search_results_order( $query ) {
if ( $query->is_main_query() && is_search() && ! is_admin() ) {
$query->query_vars['order'] = 'DESC';
$query->query_vars['orderby'] = 'relevance';
$query->query_vars['post_status'] = 'publish';
// $query->query_vars['meta_query'] = [
// 'relation' => 'OR',
// array(
// 'key' => '_prioritize_s',
// 'compare' => 'EXISTS'
// ),
// array(
// 'key' => '_prioritize_s',
// 'compare' => 'NOT EXISTS'
// )
// ];
}
return $query;
}
I originally had the orderby
set to meta_value relevance
and it did put the priority posts on top with the meta_query uncommented. However, it doesn't keep the relevance sorting intact for posts afterwards. It seems like the meta query is affecting the overall order.
I have a feeling I might need something custom like a database query instead? Doesn't seem like I'm going to get what I need by just setting the $query->query_vars
. Can anyone help out? Thanks in advance!
UPDATE: Since I haven't figured out how to alter the code I posted above, I'm trying an alternate solution. Let me know if it's the wrong way to go about it. Leaving the above code alone, I can use the found_posts
hook to alter the search results query after my pre_get_posts
function has already done it's magic. However my only issue is the second parameter that should give me the query is only giving me 10 posts when I know the results should be more than that. Why is it limiting it to 10 posts even if I set posts_per_page
to -1
in my pre_get_posts
function?
function modify_search_results_prioritized( $found_posts, $wp_query ) {
if ( is_search() ) {
//this says 10 - why?
error_out($wp_query->query_vars['posts_per_page']);
}
return $found_posts;
}
I'm trying to re-order the search results using the pre_get_posts
hook and set the orderby
parameter to relevance. I also want to put priority posts, which is based on post meta _prioritize_s
to be placed first in the results. Then all the posts after will be based on relevance as usual.
Here's the code I've been messing with:
//runs on pre_get_posts
function modify_search_results_order( $query ) {
if ( $query->is_main_query() && is_search() && ! is_admin() ) {
$query->query_vars['order'] = 'DESC';
$query->query_vars['orderby'] = 'relevance';
$query->query_vars['post_status'] = 'publish';
// $query->query_vars['meta_query'] = [
// 'relation' => 'OR',
// array(
// 'key' => '_prioritize_s',
// 'compare' => 'EXISTS'
// ),
// array(
// 'key' => '_prioritize_s',
// 'compare' => 'NOT EXISTS'
// )
// ];
}
return $query;
}
I originally had the orderby
set to meta_value relevance
and it did put the priority posts on top with the meta_query uncommented. However, it doesn't keep the relevance sorting intact for posts afterwards. It seems like the meta query is affecting the overall order.
I have a feeling I might need something custom like a database query instead? Doesn't seem like I'm going to get what I need by just setting the $query->query_vars
. Can anyone help out? Thanks in advance!
UPDATE: Since I haven't figured out how to alter the code I posted above, I'm trying an alternate solution. Let me know if it's the wrong way to go about it. Leaving the above code alone, I can use the found_posts
hook to alter the search results query after my pre_get_posts
function has already done it's magic. However my only issue is the second parameter that should give me the query is only giving me 10 posts when I know the results should be more than that. Why is it limiting it to 10 posts even if I set posts_per_page
to -1
in my pre_get_posts
function?
function modify_search_results_prioritized( $found_posts, $wp_query ) {
if ( is_search() ) {
//this says 10 - why?
error_out($wp_query->query_vars['posts_per_page']);
}
return $found_posts;
}
Share
Improve this question
edited May 3, 2019 at 20:30
RachieVee
asked May 3, 2019 at 19:33
RachieVeeRachieVee
8303 gold badges9 silver badges25 bronze badges
7
|
Show 2 more comments
2 Answers
Reset to default 2So after @Nicolai found that blurb in the WP source confirming that I can't use multiple parameters in the orderby
if I am ordering by relevance
I had to go a different route. However, this route only works if I don't care for pagination. There probably is a way to put it back, but I'm working on a different solution now and I no longer need to stay on the code I have in my question.
So I kept the function to alter the query on pre_get_posts
and removed anything regarding post meta/meta queries:
function modify_search_results_order( $query ) {
if ( $query->is_main_query() && is_search() && ! is_admin() ) {
$get_expired_events = Event_Helpers::get_expired_event_ids();
$query->query_vars['posts_per_page'] = - 1;
$query->query_vars['order'] = 'DESC';
$query->query_vars['is_search'] = true;
$query->query_vars['post__not_in'] = $get_expired_events;
$query->query_vars['orderby'] = 'relevance';
$query->query_vars['post_status'] = 'publish';
}
return $query;
}
Then in my search template, I call a function that alters the query after pre_get_posts
to look for the priority posts, unset them from the main query, and place them back on top:
//in search.php
$wp_query->posts = Search_Modify_Query::rearrange_search_query_for_priority_relevance( $wp_query->posts );
//in PHP Class
public static function rearrange_search_query_for_priority_relevance( $query ) {
//get prioritized ids
$get_prioritized = self::get_priority_search_post_ids();
$get_results_ids = [];
if ( ! is_array( $get_prioritized ) || ! $get_prioritized ) {
return $query;
}
//save all ids from current search results query
foreach ( $query as $key => $post ) {
$get_results_ids[ $key ] = $post->ID;
}
if ( ! $get_results_ids ) {
return $query;
}
//check if there are priority posts in results
if ( array_intersect( $get_prioritized, $get_results_ids ) ) {
$priority_matches = array_intersect( $get_results_ids, $get_prioritized );
$priority_query = false;
//if there are priority matches, pluck them out to put them on top
if ( $priority_matches ) {
foreach ( $priority_matches as $key => $priority_match ) {
//save these into new array first
$priority_query[ $key ] = $query[ $key ];
//then unset them from main query
unset( $query[ $key ] );
}
if ( $priority_query && is_array( $priority_query ) ) {
//then re-add them on top of main query
return array_merge( $priority_query, $query );
} else {
return $query;
}
}
}
return $query;
}
This works fine but because I needed to have ALL the results to do the comparison with my "priority post" ids vs the results ids, I had to set posts_per_page
to -1. So while it works, unless I find a way to put back pagination or write something custom, the search results will give me all of the results on search.php whether it's 5 or 500. Still, I'm placing this code here in case it helps someone else in the long run.
For my case though, I've decided to do two separate queries and I confirmed we don't care about having duplicates. So I'll query just the priority posts matching the search term above the main search results query.
Just to elaborate on what we've been talking about in the comments. I know you solved this already for your use case, so this is more for completeness’ sake and proof of concept purposes. Untested, for more information see the code comments.
add_action( 'pre_get_posts', 'prioritized_before_rest' );
function prioritized_before_rest( $q ) {
if ( ! is_admin() && $q->is_main_query() && $q->is_search() ) {
// get the search term
$search_term = $q->get( 's' );
// get the prioritized items
$prioritized = new WP_Query( [
// it is a search, so it is ordered by relevance
's' => $search_term,
// I assumed that is your condition
'meta_query' => [ [
'key' => '_prioritize_s',
'compare' => 'EXISTS',
], ],
// we want them all
'posts_per_page' => -1,
// but only ids
'fields' => 'ids',
] );
// array of ids
$prioritized_ids = $prioritized->posts;
$rest = new WP_Query( [
's' => $search_term,
// get the rest by excluding above queried priorizied items
'post__not_in' => $prioritized_ids,
'posts_per_page' => -1,
'fields' => 'ids',
] );
$rest_ids = $rest->posts;
// merge above queried ids, order matters
$prioritized_before_rest = array_merge( $prioritized_ids, $rest_ids );
// use merged array, this alone does nothing
// but to get all posts as would the unaltered query
$q->set( 'post__in', $prioritized_before_rest );
// we can set orderby to post__in though to preserve the order
// established with above queries and merging the arrays
// this keeps standard posts_per_page setting and pagination alive
$q->set( 'orderby', 'post__in' );
}
}
orderby
asmeta_value relevance
and that didn't work. It's not in the code example but I wrote in my description that I had tried that. Therelevance
isn't meta, it's a WordPressorderby
parameter you can set to sort posts by relevance for search. Hopefully that helps clear some things up. Let me know. Thanks. – RachieVee Commented May 6, 2019 at 16:16fields
parameter withids
, so$query->posts
contains an array of ids. The use of thes
parameter makes WordPress handled it like every other search, so each array is ordered by relevance. Merge the arrays, and then use the combined array to set the parameterpost__in
andorderby
withpost__in
. This is obviously untested, but I think it should work. – Nicolai Grossherr Commented May 6, 2019 at 17:16