Ok so I ended up using Relevanssi after all. But with quite a few modifications.
First of all, filtering on taxonomies was quite easy as this is built into Relevanssi, all I needed to do was change the names of my <select>
elements to the taxonomy names + change the values to the slugs:
<select name="custom_taxonomy">
<option value="some-term">Some term</option>
...
</select>
Second, to allow sorting by a custom field, I had to use the relevanssi_hits_filter
filter. Unlike pre_get_posts
or relevanssi_modify_wp_query
(which Sheikh Heera kindly suggested), this filter gets passed the array of posts and not a WP_Query object. When I tried to use the WP_Query object the order simply refused to change. Here's basically how to do your own sort with relevanssi_hits_filter
:
<?php
add_filter('relevanssi_hits_filter', 'h5b_hits_filter');
function h5b_hits_filter ($hits) {
global $wp_query;
if (isset($wp_query->query_vars['orderby']) and $wp_query->query_vars['orderby'] == 'price') {
if (count($hits[0])) {
usort($hits[0], 'h5b_sort_by_price');
}
}
return $hits;
}
function h5b_sort_by_price ($a, $b) {
$priceKey = 'price';
$aPrice = get_post_meta($a->ID, $priceKey, true);
$bPrice = get_post_meta($b->ID, $priceKey, true);
$aPrice = $aPrice ? $aPrice : 10000000;
$bPrice = $bPrice ? $bPrice : 10000000;
if ($aPrice == $bPrice) {
return 0;
}
return ($aPrice < $bPrice) ? -1 : 1;
}
This also helped with a previous problem I had in that posts that lacked the "price" key didn't show up in searches. Now they do, and the reason I give them a price of "10000000" is so that they show up after the ones that do have a price.
Finally, I also needed empty searches to work. According to the Relevanssi dev this is only supported in the Premium version, but I think I managed to work around it. First I forced WP to display the search page even if ?s was empty:
<?php
add_filter('request', 'h5b_allow_empty_search');
function h5b_allow_empty_search ($qryVars) {
if (isset($_GET['s']) and empty($_GET['s'])) {
$qryVars['s'] = ' ';
}
return $qryVars;
}
Second, I told Relevanssi to fetch all posts if ?s was empty, by default it won't fetch any posts at all. Unfortunately, in doing this the default behaviour with custom taxonomies stopped working (my code over wrote it) so I had to manually check for taxonomies in this code as well:
<?php
add_filter('relevanssi_hits_filter', 'h5b_allow_empty_search_filter');
function h5b_allow_empty_search_filter ($hits) {
if (isset($_GET['s']) and empty($_GET['s']) and !count($hits[0])) {
$taxQry = array('relation' => 'AND');
if (!empty($_GET['custom_taxonomy'])) {
$taxQry[] = array(
'taxonomy' => 'custom_taxonomy',
'field' => 'slug',
'terms' => $_GET['custom_taxonomy']
);
}
$args = array(
'numberposts' => -1,
'post_type' => 'any'
);
if (count($taxQry) > 1) {
$args['tax_query'] = $taxQry;
}
$hits[0] = get_posts($args);
}
return $hits;
}
And that's it. Everything seems to work and hopefully this will help someone who has similar requests in the future.
I really hope WP improves its ridiculous search in the future. Sorting by relevance should be something that's available in the world's most popular CMS imo.
Thanks @Sheikh Heera for pointing me in the right direction. I'm not sure who to give the right answer to though seeing as I really couldn't use the stuff you suggested.