Posted on

Using phrases in Related posts searches

I have a book review site where I’m using Relevanssi Premium and the Related posts to highlight related book reviews. The reviews have multiple taxonomies used for the related posts: tags, book author, series, characters, and places.

Messy relations

The problem is, with the default settings, the Related posts get a bit messy. This site was my main motivation to add the post part targeting – I wanted to be able to have the related posts target a specific taxonomy to get more exact results.

However, that alone wasn’t enough. It’s good that when a book is written by Dorothy L. Sayers the related posts don’t return The Wizard of Oz just because the review mentions Dorothy, but Dorothy Allison‘s works are not related to Dorothy L. Sayers either.

The same applied to other taxonomies. A book about Peter Steele isn’t related to a Dorothy L. Sayers novel featuring Lord Peter Wimsey and so on. Phrase search for multi-word taxonomy terms was required. Then the taxonomy restriction wouldn’t be necessarily required – it doesn’t matter where in the post “Lord Peter Wimsey” is mentioned, it’s definitely related.

The relevanssi_related_words hook

Relevanssi Premium gives us enough filter hooks to make building this fairly straightforward. The only hook required here is relevanssi_related_words.

From the settings, the related term sources are cleaned out. Only post titles are enabled in the Relevanssi settings. We then use the filter hook to generate the words for the taxonomies:

add_filter( 'relevanssi_related_words', 'rlv_phrase_tax_words' );

function rlv_phrase_tax_words( $words ) {
	global $post;

	$characters = rlv_process_terms( $post, 'characters', 'restrict_to_taxonomy' );
	$authors    = rlv_process_terms( $post, 'authors', 'restrict_to_taxonomy' );
	$locations  = rlv_process_terms( $post, 'locations', 'restrict_to_taxonomy' );
	$series     = rlv_process_terms( $post, 'series', 'restrict_to_taxonomy' );
	$tags       = rlv_process_terms( $post, 'post_tag', 'just_add_phrases' );

	$new_words = array_merge(
    	$characters,
    	$authors,
    	$locations,
    	$series,
    	$tags
    );
	return $words . ' ' . implode( ' ', $new_words );
}

This function does the organising: the different taxonomies are processed, the new words are merged into an array and added to the list of words the filter hook gets.

The actual work is done in the rlv_process_terms() function, so let’s take a look at that next:

function rlv_process_terms( $post, $taxonomy, $process = 'restrict_to_taxonomy' ) {
	$terms      = get_the_terms( $post, $taxonomy );
	$term_names = array();

	$callback = 'rlv_phrasify_taxonomify';
	if ( 'just_add_phrases' === $taxonomify ) {
		$callback = 'rlv_phrasify';
	}
  
	if ( $terms ) {
		$term_names = array_map(
			$callback,
			array_map(
				function( $term ) {
					return $term->name;
				},
				$terms
			),
			array_fill( 0, count( $terms ), $taxonomy )
		);
	}
	return $term_names;
}

First this function gets the terms for the current post in the taxonomy chosen. Then the terms are passed through a double-layered array_map() construction.

The inner array_map() extracts the term names from the terms (get_the_terms() returns full WP_Term objects, but we only care about the name) – it’s just a version of array_column() for objects, basically.

The outer array_map() passes the terms through a callback function, depending on which behaviour is required. The default mode is restrict_to_taxonomy, which adds phrases and the taxonomy targeting, while just_add_phrases only adds phrases.

The array_fill() just creates an array that is as long as the $terms array and is filled with the taxonomy name – this array is needed so that the rlv_phrasify_taxonomify callback function can get the taxonomy name as a parameter.

Here are the callback functions:

function rlv_phrasify( $string ) {
	if ( strpos( $string, ' ' ) !== false ) {
		return '"' . strtolower( $string ) . '"';	
	}
	return strtolower( $string );
}

function rlv_phrasify_taxonomify( $string, $taxonomy ) {
	return '{' . $taxonomy . ':' . rlv_phrasify( $string ) . '}';
}

This all gets us more precise Related posts, with less weird hits. Sometimes the results are still surprising, but it’s not possible to get perfect results without generating them manually and on a site with more than 8000 posts with new added every day, that’s just not an option.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.