Complex Queries with Relevanssi

Most search queries are straightforward: the user enters a word or two and gets results. This kind of searching is easy to do and requires hardly any configuration with Relevanssi.

But what if you need something more complicated?

Complex queries within the search form

These are things any user can do from the search form:

Phrase matching. If you put multiple search terms in quotes, they become a phrase. Searching for "rosa parks" will only find posts that have this specific phrase in them. Relevanssi looks for phrases in post titles, post content, custom fields, and taxonomy terms.

If you include multiple phrases in the search query, their relationship depends on the default search operator. If Relevanssi settings use the OR search as default, Relevanssi returns any posts with at least one of the phrases, but if the AND search is the default, all phrases must appear in the document.

Note that if the post content has HTML tags included, that will prevent the phrase matching from working. If your content reads “Rosa <em>Parks</em>“, a phrase search for "rosa parks" will not find this. The phrase must be the same, excluding differences in case.

Search operators. Premium only. Relevanssi Premium includes search operators which you can use to control the search.

  • The + operator makes the word it’s attached to mandatory. In an OR search, searching for foo bar will include all posts that have one of these words. Searching for foo +bar requires all posts returned to include the word “bar”, and they may also include “foo”. This operator has no effect in an AND search.
  • The - operator excludes a word from the results. Searching for foo -bar returns all posts that have the word “foo”, but not the word “bar”. This works both in OR and AND searches.
  • The ? operator matches a single letter inside a word. Searching for w?rd would match “word” and “ward”, but not “weird”.
  • The * operator matches any number of letters (including zero) inside a word. Searching for w*rd matches “wrd”, “word”, “ward”, “weird”, and so on.

The wildcard operators are not on by default. Read more about them and how to enable wildcard matching from Wildcard matching.

Adding query parameters to the search form

The next step is to add extra properties to the search form in the form of query parameters. One straightforward way to do this is using the search form shortcode.

These query parameters are useful for filtering search results: they restrict the results somehow. This way you can create a search form that only searches a part of your site, not everything. For filtering that users can’t control, you can use input fields with the type “hidden”. Note that this adds the query parameters to the search results page URL and if there’s, for example, a post type restriction, the user can simply remove it by deleting the parameter from the URL. For tamper-proof methods, use filter hooks (like relevanssi_modify_wp_query).

You can also add extra fields that the user can manipulate, like taxonomy dropdowns. The search form shortcode can be used to add these but you can also build the search form from HTML and PHP to get the form you want.

A full list of properties can be found in the WP_Query documentation. Some of the common options are:

  • Post type restrictions (post_type)
  • Category ID (cat) or name (category_name)
  • Tag ID (tag)
  • Author ID (author) or name (author_name)
  • Sorting (order, orderby)
  • Dates (year, monthnum, day, by_date)
  • Search operator, AND or OR (operator) (Premium only)

Using plugins to build more complex search forms is popular. FacetWP and Search & Filter Pro are the most common tools for this, and both work with Relevanssi (with some caveats).

Working with filters and PHP

For more complicated work, you can construct queries in code. There are two primary methods of constructing queries this way.

Replacing the main query. If you want to modify the main search results query, use filters. I sometimes see people do this in the wrong way, with a new WP_Query on the search results page. That’s wasteful. If you want to override the default query, don’t run a new WP_Query, but instead modify the parameters for the existing main query. The Relevanssi-specific method is the relevanssi_modify_wp_query filter hook and the general way is the pre_get_posts action hook.

add_filter( 'relevanssi_modify_wp_query', 'rlv_modify_query' );
function rlv_modify_query( $query ) {
  $query->set(
    'tax_query',
    array(
      array(
        'taxonomy' => 'genre',
        'terms'    => array( 'poetry', 'nonfiction' ),
        'field'    => 'slug',
      ),
    ),
  );
  return $query;
}

Running a new query. If you do need to run a new query (for example, you want to run a query outside the search results template), the best way to make Relevanssi take over is to set the relevanssi parameter to true. Relevanssi will take over all queries like that, with no adverse effects in case Relevanssi is inactive.

$args = array(
  's'              => 'foo bar baz',
  'post_type'      => 'publish',
  'posts_per_page' => 20,
  'relevanssi'     => true,
);
$query = new WP_Query( $args );
// Now $query->posts has Relevanssi results.

These methods allow you access to very powerful filtering capabilities. The most notable are using taxonomy queries with the tax_query parameter, creating more complex custom field queries with meta_query and using date ranges with date_query. See the WP_Query Codex documentation for instructions on how these are done.

There are some limitations to the tax_query queries Relevanssi understands, but in most cases even complex queries work as long as they are constructed with care and using the exactly right nesting of arrays. You can combine multiple taxonomies using Boolean logic, so using an example from the Codex, you can create queries that fetch posts that are in the “quotes” category or both have the “quote” post format and are in the “wisdom” category – and match a specific search term.

$args = array(
  's'          => 'carrot',
  'post_type'  => 'post',
  'tax_query'  => array(
    'relation' => 'OR',
    array(
      'taxonomy' => 'category',
      'field'    => 'slug',
      'terms'    => array( 'quotes' ),
    ),
    array(
      'relation' => 'AND',
      array(
        'taxonomy' => 'post_format',
        'field'    => 'slug',
        'terms'    => array( 'post-format-quote' ),
      ),
      array(
        'taxonomy' => 'category',
        'field'    => 'slug',
        'terms'    => array( 'wisdom' ),
      ),
    ),
  ),
  'relevanssi' => true,
);
$query = new WP_Query( $args );
// $query->posts now has Relevanssi-generated wisdom on carrots.

The same goes with meta queries, you can create some fairly complicated constructions. However, here I would recommend caution: meta queries are usually the slowest thing in the WP_Query world, so a complex meta query can be prohibitively slow to run. In the more complex cases, it’s faster to just fetch all the posts and then use a relevanssi_hits_filter function to weed out the unwanted posts.