thumbnail

Elasticsearchのような検索機能を提供するためのWordPressのプラグインを開発しました

WordPressはデフォルトでキーワード検索機能がありますが、精度はあまりよくありません。

具体的には、例えば「php wordpress 管理サイト」のように検索した場合、これらすべての単語を含んでいる記事しか引っかかりません。

「wordpress」や「管理画面」が含まれる記事に検索者が知りたい情報があった可能性もありますが、検索者はあきらめて別のサイトに移動してしまうかもしれません。

Elasticsearchと呼ばれる全文検索エンジンの場合、検索ワードをすべて含んでいなくても検索結果に表示させることができます。

検索ワードを含むかではなく、検索ワードにどれくらいマッチしているかを評価し、評価の高い順に結果を返してくれるためです。

Elasticsearchは検索を劇的に改善してくれますが、使用するにはサーバにインストールしたりElastic Cloudのようなクラウドサービスを利用する必要があります。

今回作成したプラグインはそのElasticsearchのような全文検索を可能にするものです。

通常のプラグインと同様に導入することができ、別途必要なものはありません。

動作要件

PHP5.6以上が必要です。

導入手順

  1. githubからZIPをダウンロード ダウンロード
  2. wp-content/plugins に展開
  3. 管理画面から有効化
  4. 左メニュー「Related Post」 > 「進捗」から「インデックス処理を有効化」を押下

以上の手順で有効化しますが、処理が終わるまではキーワード検索に使用されません。

進捗状況は上記画面から確認できます。

完了するとインデックスされた結果が使用されるようになります。

特徴

ElasticsearchのようなAnalyzer

日本語の分かち書きにも対応しています。

Okapi BM25 アルゴリズムによる文章類似度計算

主な設定

  • バイグラムトークナイザーを使用するかどうか
    • true:
      文字列を2文字ずつ取り出して文章を構成する単語として使用します。
    • false:
      • ヤフーの設定をしている場合:
        ヤフーの日本語形態素解析サービスを使用して単語を分割します。
        利用制限があるため検索で使用する場合に制限に引っかかる可能性があります。
      • ヤフーの設定をしていない場合:
        Igoという形態素解析器を使用します。
        ローカルで動作させるため利用制限等はありませんが処理速度等はサーバに依存します。

関連記事を表示

インデックスされた情報は関連記事の表示に利用することが可能です。

関連記事を取得するクエリ生成前に以下のアクションを発行します。

do_action( 'related_post/on_related_post' );

このアクションが発行された後の一回のみ、WP_Queryはこのプラグインで算出された関連記事を返します。

例えば関連記事用のテンプレートを以下のように呼び出しているテーマの場合、

get_template_part('related-list');

functions.php 等に記述するコードは以下のようになります。

add_action( 'get_template_part_related-list', function () {
	do_action( 'related_post/on_related_post' );
} );

このプラグインの functions.php にはあらかじめ Cocoon 及び Simplicity2 用のコードが記述されているため、 それらのテーマを使用している方は別途設定は必要ありません。

インデックス対象を変更

デフォルトでは記事のタイトルと本文がインデックスの対象になっており、タイトルに重みづけがされています。

str_repeat( $post->post_title . ' ', 3 ) . $post->post_content;

「本文のみ」「タイトルのみ」「タイトルと本文とタグ」を対象とする設定があらかじめ別途用意されています。

以下のようなプログラムを functions.php などに記述することでそれらに変更することが可能です。

add_filter( 'related_post/extractor', function () {
	return 'content';            // 本文のみ
//	return 'title';              // タイトルのみ
//	return 'title_content_tags'; // タイトルと本文とタグ
} );

さらに以下のようなプログラムを記述することで、カスタムフィールドなどを含め自由に対象を設定することが可能です。

add_filter( 'related_post/extractor', function () {
	return false;
} );
add_filter( 'related_post/extractor_result', function ($d, $post) {
	return $post->title . ' ' . get_post_meta($post->ID, 'custom_field_key', true);
} );

関連記事の表示を変更

related_post/related_posts_content をフィルタすることで関連記事の表示を変更することが可能です。

変更例

add_filter( 'related_post/related_posts_content', function (
	/** @noinspection PhpUnusedParameterInspection */
	$content, $control, $title, $post, $related_posts
) {
	/** @var \Related_Post\Classes\Models\Control $control */
	/** @var string $title */
	/** @var array $related_posts */
	ob_start();
	?>
    <style>
        .related_posts_content {
            margin: 10px;
            padding: 10px;
            border: #ccc 1px solid;
            -webkit-transition: all 0.5s ease;
            -moz-transition: all 0.5s ease;
            -ms-transition: all 0.5s ease;
            -o-transition: all 0.5s ease;
            transition: all 0.5s ease;
            background: white;
        }

        .related_posts_content:hover {
            -webkit-box-shadow: #ccc 0 0 16px;
            -moz-box-shadow: #ccc 0 0 16px;
            box-shadow: #ccc 0 0 16px;
            background: #f0ffff;
        }

        .link-item {
            letter-spacing: -1em;
        }

        .link-item .thumbnail {
            display: inline-block;
            width: 20%;
            margin: 0;
            vertical-align: middle;
        }

        .link-item .thumbnail img {
            vertical-align: middle;
        }

        .link-item .title {
            display: inline-block;
            -webkit-box-sizing: border-box;
            -moz-box-sizing: border-box;
            box-sizing: border-box;
            width: 80%;
            padding: 1em;
            margin: 0;
            font-weight: bold;
            letter-spacing: normal;
        }
    </style>
    <div class="related_posts">
        <h3 class="related_posts_title">
			<?php $control->h( $title ); ?>
        </h3>
        <div class="related_posts_wrap">
			<?php foreach ( $related_posts as $related_post ): ?>
				<?php /** @var WP_Post $related_post */ ?>
                <div class="related_posts_content">
					<?php $control->url( get_permalink( $related_post->ID ), <<< EOS
                    <div class="link-item">
                        <div class="thumbnail">
                            {$control->get_thumbnail( $related_post->ID )}
                        </div>
                        <div class="title">
                            {$related_post->post_title}
                        </div>
                    </div>
EOS
						, false, false, [], true, false ); ?>
                </div>
			<?php endforeach; ?>
        </div>
    </div>
	<?php

	$view = ob_get_contents();
	ob_end_clean();

	return $view;
}, 10, 5 );

以下のようなサムネイル付きの表示に変更されます。

要望・バグ報告等

まとめ

検索はサイト内の別の記事を見てもらえるチャンスであり、検索結果に何もでなくなってしまうのは大きな損失になってしまいます。

分散処理もできるElasticsearchの導入がおススメですが、それが困難な場合の代わりにはぜひプラグインを導入してみてください。


Makefile で 動的にコマンドを変える方法
next article
arrow