Kirjavinkit.fi:n hakutulossivun parantaminen

Lähtötilanne

Kirjavinkit.fi:ssä haku on keskeinen tekijä sivuilla, onhan siellä enemmän tai vähemmän kuranttia sisältöä noin 9000 artikkelin verran. Kun kyse on kirja-arvosteluista, vanhakin tavara on käyttökelpoista ja syytä pitää saatavilla, eikä siinä juuri muu auta kuin haku.

Käytössä on luonnollisesti Relevanssi (Kirjavinkit on itse asiassa syy sille, miksi Relevanssi aikoinaan ylipäänsä kehitettiin). Lähtötilanne näyttää tältä:

Kirjavinkit.fi:n hakutulos ennen muutosprosessia

Tästä näkyy yksi hakutulosten ongelmista. Ensimmäinen tulos on Agatha Christietä käsittelevä kirja, oikein kelpo osuma. Sen jälkeen tulee Agatha Christien kirjailijasivu, sitten Agatha Christie kirjassa esiintyvänä henkilönä ja neljänneksi Agatha Christien itsensä kirjoittama kirja. Kaikki ovat hyviä osumia, mutta näyttäähän tämä vähän rujolta, kun osasta on pitkälti tekstiä ja osasta ei mitään. Lisäksi on vähän hankala hahmottaa kahden Agatha Christie -tuloksen välistä eroa, vaikka ensimmäisen kohdalla ”Katso kirjailijalta vinkatut teokset”-linkki vähän antaakin suuntaa siitä, mistä on kyse.

Tuumin, että käytännöllisempää olisi, jos hakutuloksissa näytettäisiin ensin tiiviisti tulosjoukossa mukana oleva sisältö, joka ei ole kirjavinkkejä: siis siistit listat tuloksista löytyvistä kirjailijoista, henkilöistä, avainsanoista, paikoista ja muista taksonomiatermeistä, jotka on otettu hakuun mukaan. Tämä onneksi onnistuu Relevanssin avulla aivan mainiosti.

(Tämä taksonomiatermien sisällyttäminen hakuun on Relevanssi Premiumin ominaisuus, jota ei löydy Relevanssin ilmaisversiosta.)

Kaksi eri tapaa ratkaista ongelma

Lähestymistapoja on oikeastaan kaksi. Melko vaivatonta olisi lajitella hakutulokset niin, että muu kuin artikkelisisältö nostetaan hakutulosten etusijalle, josta ne olisi sitten helppo näyttää etusijalla tuloksissa. Tämä aiheuttaa kuitenkin ongelmia hakutulosten sivutuksen suhteen: jos joka sivulla näytetään 20 tulosta ja ensimmäisellä sivulla on ensin viisitoista ei-artikkelia ja viisi artikkelia, niin näyttäähän se vähän hölmöltä. Parempi olisi näyttää aina 20 artikkelia, riippumatta siitä, mikä määrä ei-artikkelisisältöä näytetään. Tähän on onneksi ratkaisuja olemassa.

Toinen vaihtoehto on tehdä kaksi erillistä hakua. Päähaku hakisi silloin vain artikkeleita ja sen sivutus voisi toimia normaaliin tapaan ongelmitta, ja erillisellä haulla noukittaisiin sitten aiheeseen liittyvät ei-artikkelit näkyville. Tässä olisi etuna se, että ei-artikkelit olisi helppo näyttää jokaisella hakutulossivulla, mutta toisaalta sitten tarvittaisiin kaksi erillistä hakua, mikä taas vie resursseja.

Hakutulosten lajitteleminen

Päädyin ensimmäiseen ratkaisuun. Alku on yksinkertainen. On helppoa pistää Relevanssi lajittelemaan taksonomiatermit hakutuloksissa ennen artikkeleita:

add_filter( 'relevanssi_comparison_order', 'rlv_taksonomiat_ensin' );
add_filter( 'relevanssi_modify_wp_query', 'rlv_orderby' );

/**
 * Lajittelee sisällön artikkelilajin mukaan.
 *
 * @param array $order_array Artikkelilajit järjestyksessä.
 */
function rlv_taksonomiat_ensin( $order_array ) {
	$order_array = array(
		'kirjailijat' => 0,
		'henkilot'    => 1,
		'paikat'      => 2,
		'sarja'       => 3,
		'post'        => 4,
		'page'        => 4,
	);
	return $order_array;
}

/**
 * Säätää orderby-parameterin arvoon 'post_type'.
 *
 * @param WP_Query $query WP_Query-objekti.
 */
function rlv_orderby( $query ) {
	$query->set(
		'orderby',
		array(
			'post_type' => 'asc',
			'relevance' => 'desc',
		)
	);
	return $query;
}

Näillä konsteilla saadaan taksonomiat tulosten kärkeen ja loppuun post– ja page-tyyppiset tulokset relevanssin mukaiseen järjestykseen.

On myös verrattain yksinkertaista tulostaa hakutulossivulla nämä osumat ennen muita osumia:

<?php
$artikkelilajit = array(
  'kirjailijat' => 'Kirjailijat',
  'henkilot'    => 'Henkilöt',
  'paikat'      => 'Paikat',
  'sarja'       => 'Sarjat',
);
$nykyinen       = '';
$offset         = 0;

while ( have_posts() ) {
  the_post();
  if ( in_array( $post->post_type, array( 'post', 'page' ), true ) ) {
    if ( ! empty( $nykyinen ) ) {
      echo '</p>';
    }
    break;
  }
  if ( $post->post_type !== $nykyinen ) {
    if ( ! empty( $nykyinen ) ) {
      echo '</p>';
    }
    echo '<p class="hakutulos"><span class="haku_otsake">'
      . esc_html( $artikkelilajit[ $post->post_type ] )
      . ':</span> ';
    $nykyinen = $post->post_type;
  }
  printf(
    '<a href="%s">%s</a> ',
    relevanssi_get_permalink( $post->ID ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
    esc_html( apply_filters( 'single_cat_title', $post->post_title ) )
  );
  $offset++;
}
rewind_posts();

echo '<div class="grid-wrapper">';
while ( have_posts() ) :
  the_post();
  $offset--;
  if ( $offset >= 0 ) {
    continue;
  }

  get_template_part( 'template-parts/content', 'search' );

endwhile;
echo '</div><!-- .grid-wrapper -->';
?>

Tässä käydään läpi löydetyt tulokset ja mikäli artikkelilaji ($post->post_type) on joku muu kuin post tai page, tulostellaan otsikoita ja tulokset vähän eri tavalla. Tällaiset erilliset osumat lasketaan ja lopuksi rewind_posts() kelaa tulokset alkuun.

Varsinaisessa loopissa ohitetaan sitten ensimmäiset $offset osumaa.

Artikkelien lukumäärä ja sivutus

Nyt taksonomiatermit nousevat hienosti tuloksissa kärkeen ja ne näytetään ensimmäisellä hakutulossivulla ennen varsinaisia artikkeliosumia. Ongelmaksi tulee kuitenkin artikkelien määrän väheneminen ensimmäisellä tulossivulla. Asetuksista on säädetty hakutulossivulla näytettäväksi 20 artikkelia, mutta jos taksonomiaosumia on vaikkapa kymmenen, ensimmäiselle sivulle tulee vain kymmenen artikkelia. Se ei ole toivottu lopputulos ja jos taksonomiaosumia on yli 20, törmätään luultavasti rumiin ongelmiin.

Tähän on melko suoraviivainen ratkaisu:

add_filter( 'relevanssi_hits_filter', 'kirjavinkit_laske_ei_artikkelit' );
function kirjavinkit_laske_ei_artikkelit( $osumat ) {
	global $kirjavinkit_ei_artikkelit, $wp_query;
	$kirjavinkit_ei_artikkelit = 0;
	foreach ( $osumat[0] as $osuma ) {
		if ( ! in_array( $osuma->post_type, array( 'post', 'page' ), true ) ) {
			$kirjavinkit_ei_artikkelit++;
		} else {
			break;
		}
	}

	if ( 0 === $wp_query->query_vars['paged'] ) {
		$wp_query->query_vars['posts_per_page'] += $kirjavinkit_ei_artikkelit;
	}

	if ( $wp_query->query_vars['paged'] > 0 ) {
		$wp_query->query_vars['offset'] = $kirjavinkit_ei_artikkelit;
	}

	return $osumat;
}

Tässä lasketaan kuinka monta taksonomiatermiä hakutulosten listan alussa on ja arvo kirjataan globaaliin $kirjavinkit_ei_artikkelit-muuttujaan, koska sitä tullaan vielä tarvitsemaan muualla. Sen jälkeen jos ollaan etusivulla eli paged-arvo on 0, lisätään näytettävien artikkelien määrää (posts_per_page) tällä arvolla, tai jos ollaan myöhemmillä sivuilla, kasvatetaan offset-arvoa saman verran.

Tämä toimii niinkuin pitää: etusivulla näytetään oikea määrä artikkeleita ja sivutus juoksee oikein – siihen asti kunnes ei juoksekaan.

Sivutuksen virheiden korjaaminen

Lopulta tässä nimittäin käy niin, että hyvällä todennäköisyydellä haku ilmoittaa että hakutulossivuja on vaikkapa seitsemän ja kun menet sivulle seitsemän, koko sivua ei löydykään, kun tulokset mahtuivatkin vain kuudelle sivulle, koska niistä on itse asiassa poistettu kaikki taksonomiatermit.

Ratkaisu asiaan on lopulta melko yksinkertainen. Teema käyttää sivutukseen WordPressin the_posts_pagination()-funktiota. Se taas tekee sivutuksen lopulta paginate_links()-funktiolla, joka taas hakee sivujen maksimimäärän $wp_query->max_num_pages-muuttujasta, jonka Relevanssi asettaa (ja laskee väärin, koska sotkemme laskelmia). Asia ratkeaa siis niin, että korjataan $wp_query->max_num_pages oikeaan arvoon.

Missä kohtaa korjauksen sitten tekisi? Pohdin hetken jonkun action hookin käyttämistä, esimerkiksi loop_start, mutta lopulta tein helpommin. Teemani käyttää sivutukseen omaa sanse_posts_pagination()-funktiota, joka vain kutsuu the_posts_pagination()-funktiota oikeilla parametreillä (jotta eri sivupohjissa voidaan kutsua omaa funktiota ilman parametrejä ja sivutukseen tulee aina samat parametrit ja jos parametrejä joskus haluaa muuttaa, voi muuttaa vain yhdestä kohtaa). Lisätään vain oma funktio kääreeksi teeman funktiolle ja säädetään siinä sivujen lukumäärä oikein:

/**
 * Korjaa sivutuksen oikein.
 *
 * @global WP_Query $wp_query                  WP_Query-objekti.
 * @global int      $kirjavinkit_ei_artikkelit Ei-artikkeliosumien määrä.
 */
function kirjavinkit_posts_pagination() {
	global $wp_query, $kirjavinkit_ei_artikkelit;

	$posts_per_page          = 0 === $wp_query->query_vars['paged']
		? $wp_query->query_vars['posts_per_page'] - $kirjavinkit_ei_artikkelit
		: $wp_query->query_vars['posts_per_page'];
	$wp_query->max_num_pages = ceil(
		( $wp_query->found_posts - $kirjavinkit_ei_artikkelit ) / $posts_per_page
	);

	sanse_posts_pagination();
}

Kaikilla sivuilla löydettyjen artikkelien lukumäärästä ($wp_query->found_posts) on tarpeen vähentää ei-artikkelien määrä (joka laskettiin aikaisemmin ja sijoitettiin muuttujaan $kirjavinkit_ei_artikkelit). Lisäksi haun ensimmäisellä sivulla muutettiin posts_per_page-arvoa ja tämä muutos pitää tässä kohtaa perua, jotta sivutus menee oikein.

Sen jälkeen annetaan sanse_posts_pagination()-funktion tehdä sivutus ja kas vain, se menee tismalleen oikein.

Lopputulos

Tältä se sitten näyttää:

Kirjavinkkien parannettu hakutulos

Paljon parempi! Mutta ei tämä nyt vielä ihan täydellinen ole. Jos taksonomiaosumia tulee paljon, ne valtaavat aika lailla tilaa:

Kirjavinkkien hakutulos, paljon taksonomiatermiosumia

Tähän täytynee vielä kehitellä jotain. Yksi vaihtoehto olisi piilottaa javascriptillä isoin osa listasta, näyttää rivi tai pari ja sitten joku ”näytä loput”-linkki, jolloin pitkä nimilista ei veisi hirveästi tilaa. Niissä tapauksissa kun hakija ei ole kiinnostunut kyseisen lajin osumista, ne eivät olisi häiriöksi. Tämä menee kuitenkin jo sen verran aiheesta ohi, että jätän jatkon harjoitukseksi lukijalle.

Vastaa

Sähköpostiosoitettasi ei julkaista.

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