サイト内全文検索機能(SearchEstraierプラグイン)

SearchEstraierプラグインは、HyperEstraierを利用したサイト内全文検索機能を提供します。 従来のSQLを使った複数条件指定検索に加え、数万ページでも高速に動作する全文検索機能を利用できるようになります。キーワードにマッチするコンテンツをスコア順に抽出できます。

このWebサイトのドキュメント検索機能は、このプラグインを利用して実装されています。

サイト内全文検索

設置とインストール

  • HyperEstraier と MeCab(オプション)をインストールします。
  • estcmd のパスが /usr/local/bin/estcmd でない時、環境変数「searchestraier_estcmd_path」に estcmd のパスを追加します。
  • mecab のパスが /usr/local/bin/mecab でない時、環境変数「searchestraier_mecab_path」に mecab のパスを追加します。
  • プラグインを有効化します。
  • 検索対象のスコープのプラグイン設定で、インデックスのパスを入力し、「検索を有効化」にチェックを入れます。
  • tools/worker.phpを実行します。
  • ビューを作成します。サンプルは plugins/SearchEstraier/theme/views/ 以下に含まれています。
  • ビューに対する URLマッピングでアーカイブ種別を「インデックス」とし、ファイル出力を「ダイナミック」とします。

プラグイン設定

  • 検索を有効化(スコープでの全文検索を有効化します)
  • クッキーをスコープ固有にする(ユーザーの閲覧履歴を保存するクッキーをスコープ毎に設定します)
  • 動的ページを検索対象にする ※1 (URLマップのファイル出力が「ダイナミック」「オンデマンド」のコンテンツを検索対象にします)
  • インデックスのパス ※2 (CMSから書込み可能な検索インデックスを保存するディレクトリを指定します)
  • アーカイブ種別(検索対象とするアーカイブ種別を指定します)
  • 文書のタイトル(文書のタイトルをどこから取得するかを指定します。「自動」とするとアーカイブのタイトルを文書のタイトルとします)
  • サムネイルを作成 / 幅 ※3 (検索結果にアイキャッチとして表示させるサムネイルを作成する場合に指定します)
  • ※1 ダイナミックコンテンツを検索対象にする場合、コンテンツ更新時に内部的に再構築を行います。ダイナミック、オンデマンド指定のURLマップで、可能なものは静的に変更することをお勧めします。
  • ※2 <mt:property name="support_dir"> は、環境変数 support_dir で指定するパスです。指定のない時、アプリケーションの直下の support/ ディレクトリとなります。
    異なるスコープで同一のインデックスを指定することで、スコープをまたがった検索が可能となります。
  • ※3 metaタグ「og:image」がある場合はその画像、ない場合は本文エリアに出現する最初の img要素からサムネイルを作成します。SVGには対応していません。

高度な設定

MeCabがインストールされている時、文書内のキーワードを自動抽出して、レコメンドAPIによる「閲覧履歴による興味関心」の精度を上げることができます。
あわせて php-mecabのインストールを推奨します(必須ではありません)。

以下の環境変数を config.json の config_settings に記述します。

  • searchestraier_auto_keywords を true
  • searchestraier_mecab_path に mecab コマンドのパス
  • searchestraier_end_of_sentense(省略可能)に文章の終わりを識別する文字。デフォルトは「。」
  • searchestraier_min_word_len(省略可能)にキーワードの最小文字数。デフォルトは3文字以上。
  • searchestraier_min_word_cnt(省略可能)にキーワードの出現回数。デフォルトは3回以上出現したキーワードを対象にします。
  • searchestraier_word_multibyte_only(省略可能)に true もしくは false。抽出するキーワードをマルチバイト文字列のみとします。デフォルトは true。
  • searchestraier_mecab_userdic(省略可能)に mecabのユーザー辞書のパス。

検索フォームと検索結果のビュー

以下のようなビューを作成します。

<form method="GET" action="<mt:var name="current_archive_url">">
<div class="form-inline">
  <label> キーワード
  <input type="text" value="<mt:var name="query" escape>" name="query" "></label>
  <label>
    <input type="radio" name="and_or" value="AND" <mt:if name="request.and_or" eq="AND">checked</mt:if>>
    AND
  </label>
  <label>
    <input type="radio" name="and_or" value="OR" <mt:if name="request.and_or" eq="OR">checked</mt:if>>
    AND
  </label>
  <button type="submit">検索</button>
</div>
</form>
<mt:var name="request.query" setvar="query">
<mt:estraiersearch phrase="$query" prefix="estraier_" and_or="AND" default_limit="10" snippet_width="200" workspace_ids="0,1">
  <mt:if name="__first__">
    <p>
    「<mt:var name="query" escape>」の検索結果( <mt:var name="search_hit">件ヒットしました )
    </p>
<ul class="list-unstyled"></mt:if>
  <mt:if name="estraier_thumbnail_square">
    <mt:var name="estraier_thumbnail_square" setvar="thumbnail_url">
  <mt:else>
    <mt:setvarblock name="thumbnail_url"><mt:var name="theme_static">website/images/no-image.png</mt:setvarblock>
  </mt:if>
  <li class="mb-2"><div class="d-flex"><div class="thumbnail"><img src="<mt:var name="thumbnail_url" escape>" width="80"  height="80" alt=""></div>
  <div><a href="<mt:var name="estraier_uri" escape>"><strong><mt:var name="estraier_title" escape></strong></a>
  <p class="snippet"><mt:var name="estraier_snippet"></p></div></div>
  </li>
<mt:if name="__last__"></ul>
  </mt:if>
</mt:estraiersearch>
<mt:if name="query">
<mt:unless name="search_hit">
<p>「<mt:var name="query" escape>」にマッチするページはありませんでした。</p>
</mt:unless>
</mt:if>

ページネーションのビュー

<mt:if name="estraier_pagertotal" gt="1">
<mt:for from="1" to="$estraier_pagertotal">
<mt:if name="__first__">
<nav aria-label="ページネーション">
  <ul>
    <li>
      <a aria-label="先頭へ" href="<mt:var name="current_archive_url">?query=<mt:var name="query" escape encode_url="1"><mt:if name="request.and_or">&amp;and_or=<mt:var name="request.and_or" escape></mt:if>">
        &laquo;
      </a>
    </li>
    <mt:if name="request.offset">
      <a aria-label="前へ" href="<mt:var name="current_archive_url">?query=<mt:var name="query" escape encode_url="1">&amp;offset=<mt:var name="estraier_prevoffset"><mt:if name="request.and_or">&amp;and_or=<mt:var name="request.and_or" escape></mt:if>">
        &lsaquo;
      </a>
    </mt:if>
</mt:if>
    <li class="<mt:if name="__value__" eq="$estraier_currentpage"> active</mt:if>">
    <a href="<mt:var name="current_archive_url">?query=<mt:var name="query" escape encode_url="1">&amp;offset=<mt:math eq="x * y" x="$__index__" y="$estraier_limit"><mt:if name="request.and_or">&amp;and_or=<mt:var name="request.and_or" escape></mt:if>"><mt:var name="__value__"></a>
    </li>
<mt:if name="__last__">
    <mt:if name="estraier_nextoffset">
      <a aria-label="次へ" href="<mt:var name="current_archive_url">?query=<mt:var name="query" escape encode_url="1">&amp;offset=<mt:var name="estraier_nextoffset"><mt:if name="request.and_or">&amp;and_or=<mt:var name="request.and_or" escape></mt:if>">
        &rsaquo;
      </a>
    </mt:if>
    <li>
      <a aria-label="最後へ" href="<mt:var name="current_archive_url">?query=<mt:var name="query" escape encode_url="1">&amp;offset=<mt:math eq="( x - 1 ) * y" x="$estraier_pagertotal" y="$estraier_limit">">
        &raquo;
      </a>
    </li>
  </ul>
</nav>
</mt:if>