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;
}
So 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.