検索APIの利用方法

【連載】

簡単導入! OSS全文検索サーバFess入門

【第16回】検索APIの利用方法

[2019/09/12 07:30]菅谷 信介 ブックマーク ブックマーク

  • 開発ソフトウェア

開発ソフトウェア

今回はFessが提供する検索APIを利用して、クライアントサイドで検索と結果表示を行う方法をご紹介します。Fessの検索APIを利用することで、既存のウェブサイトにHTMLの変更だけで組み込むことも可能になります。

JSON API

Fessは通常のHTMLによる検索表現以外にAPIとしてJSONによる検索結果の応答が可能です。JSON APIを利用することで、既存のシステムから検索結果だけを問い合わせすることも簡単に実現できます。

検索結果を開発言語に依存しない形式で扱えるので、FessをJava以外のシステムに統合することも容易にできます。

Fessの提供しているAPIがどのような応答を返してくるかについてはFessのサイトを参照してください。

Fessは検索エンジンとしてElasticsearchを利用していますが、ElasticsearchとFessのAPIは異なります。

FessのAPIを利用するメリットは、検索ログの管理や閲覧権限の制御など、Fess固有の機能を利用できることが挙げられます。

ドキュメントクロールの仕組みをゼロから独自に開発したい場合はElasticsearchを利用するのが良いと思いますが、簡単に検索機能を追加したい場合はFessを利用する方が多くの開発コストを削減できます。

JSON APIを利用した検索サイトの構築

CORS

JSONでアクセスする際にはSame-Originポリシーに注意する必要があります。HTMLを出力するサーバとFessサーバが異なるドメインに存在する場合はCORS(Cross-Origin Resource Sharing)を利用する必要があります。

今回はHTMLが置いてあるサーバとFessサーバが異なるドメインにある想定で説明します。

Fessの設定

Fess 13.2.0を利用します。

FeesのダウンロードとインストールはFessのインストールのページを参照してください。

FessはCORSに対応しており、設定値はapp/WEB-INF/classes/fess_config.propertiesで設定可能です。デフォルトでは以下が設定されています。

api.cors.allow.origin=*
api.cors.allow.methods=GET, POST, OPTIONS, DELETE, PUT
api.cors.max.age=3600
api.cors.allow.headers=Origin, Content-Type, Accept, Authorization, X-Requested-With
api.cors.allow.credentials=true

今回はこのまま利用しますが、設定を変更した場合はFessを再起動をしてください。

作成するファイル

今回はHTML上でJavaScriptを利用して検索処理を実装します。わかりやすく実装するため、jQueryを利用しています。

作成するファイルは以下になります。

  • 検索フォームと検索結果を表示するHTMLファイル「index.html」
  • Fessサーバと通信するJSファイル「fess.js」

今回の構築例では以下の機能を実装しています。

  • 検索ボタンで検索リクエストの送信
  • 検索結果の一覧の表示
  • 検索結果のページング処理

HTMLファイルの作成

まず、検索フォームと検索結果を表示するHTMLを作成します。今回は以下の内容のHTMLファイルを利用します。

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>検索サイト</title>
</head>
<body>
<div id="header">
  <form id="searchForm">
    <input id="searchQuery" type="text" name="query" size="30"/>
    <input id="searchButton" type="submit" value="検索"/>
    <input id="searchStart" type="hidden" name="start" value="0"/>
    <input id="searchNum" type="hidden" name="num" value="20"/>
  </form>
</div>
<div id="subheader"></div>
<div id="result"></div>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script type="text/javascript" src="fess.js"></script>
</body>
</html>

bodyタグ以下を見ていくと、id属性がheaderのdivタグの箇所で検索入力欄と検索ボタンを配置しています。hiddenフォームでは表示開始位置(start)と表示件数(num)を保持しています。

検索リクエスト送信後にJavaScriptでstartとnumの値は更新されますが、今回のサンプルコードでは表示件数を変更する機能はないので、numの値は変更されません。

次のsubheaderのdivタグの箇所で検索にヒットした件数などの情報が表示されます。resultのdivタグでは検索結果およびページングリンクが表示されます。

最後にjQueryのJSファイルと今回作成するfess.jsを読み込みます。jQueryのJSファイルを「index.html」と同じディレクトリに保存しても構いませんが、今回はGoogleのCDN経由で取得するようにしています。

JSファイルの作成

次にFessサーバと通信して検索結果を表示するJSファイル「fess.js」を作成します。以下の内容で「fess.js」を作成し、「index.html」と同じディレクトリに配置します。

$(function(){
    // Fess の URL
    var baseUrl = "http://SERVERNAME:8080/json/?q=";
    // 検索ボタンのjQueryオブジェクト
    var $searchButton = $('#searchButton');

    // 検索処理関数
    var doSearch = function(event){
      // 表示開始位置、表示件数の取得
      var start = parseInt($('#searchStart').val()),
          num = parseInt($('#searchNum').val());
      // 表示開始位置のチェック
      if(start < 0) {
        start = 0;
      }
      // 表示件数のチェック
      if(num < 1 || num > 100) {
        num = 20;
      }
      // 表示ページ情報の取得
      switch(event.data.navi) {
        case -1:
          // 前のページの場合
          start -= num;
          break;
        case 1:
          // 次のページの場合
          start += num;
          break;
        default:
        case 0:
          start = 0;
          break;
      }
      // 検索フィールドの値をトリムして格納
      var searchQuery = $.trim($('#searchQuery').val());
      // 検索フォームが空文字チェック
      if(searchQuery.length != 0) {
        var urlBuf = [];
        // 検索ボタンを無効にする
        $searchButton.attr('disabled', true);
        // URL の構築
        urlBuf.push(baseUrl, encodeURIComponent(searchQuery),
          '&start=', start, '&num=', num);
        // 検索リクエスト送信
        $.ajax({
          url: urlBuf.join(""),
          dataType: 'json',
        }).done(function(data) {
          // 検索結果処理
          var dataResponse = data.response;
          // ステータスチェック
          if(dataResponse.status != 0) {
            alert("検索中に問題が発生しました。管理者にご相談ください。");
            return;
          }

          var $subheader = $('#subheader'),
              $result = $('#result'),
              record_count = dataResponse.record_count,
              offset = 0,
              buf = [];
          if(record_count == 0) { // 検索結果がない場合
            // サブヘッダー領域に出力
            $subheader[0].innerHTML = "";
            // 結果領域に出力
            buf.push("<b>", dataResponse.q, "</b>に一致する情報は見つかりませんでした。");
            $result[0].innerHTML = buf.join("");
          } else { // 検索にヒットした場合
            var page_number = dataResponse.page_number,
                startRange = dataResponse.start_record_number,
                endRange = dataResponse.end_record_number,
                i = 0,
                max;
            offset = startRange - 1;
            // サブヘッダーに出力
            buf.push("<b>", dataResponse.q, "</b> の検索結果 ",
              record_count, " 件中 ", startRange, " - ",
              endRange, " 件目 (", dataResponse.exec_time,
                " 秒)");
            $subheader[0].innerHTML = buf.join("");

            // 検索結果領域のクリア
            $result.empty();

            // 検索結果の出力
            var $resultBody = $("<ol/>");
            var results = dataResponse.result;
            for(i = 0, max = results.length; i < max; i++) {
              buf = [];
              buf.push('<li><h3 class="title">', '<a href="',
                results[i].url_link, '">', results[i].title,
                '</a></h3><div class="body">', results[i].content_description,
                '<br/><cite>', results[i].site, '</cite></div></li>');
              $(buf.join("")).appendTo($resultBody);
            }
            $resultBody.appendTo($result);

            // ページ番号情報の出力
            buf = [];
            buf.push('<div id="pageInfo">', page_number, 'ページ目<br/>');
            if(dataResponse.prev_page) {
              // 前のページへのリンク
              buf.push('<a id="prevPageLink" href="#"><<前ページへ</a> ');
            }
            if(dataResponse.next_page) {
              // 次のページへのリンク
              buf.push('<a id="nextPageLink" href="#">次ページへ>></a>');
            }
            buf.push('</div>');
            $(buf.join("")).appendTo($result);
          }
          // ページ情報の更新
          $('#searchStart').val(offset);
          $('#searchNum').val(num);
          // ページ表示を上部に移動
          $(document).scrollTop(0);
        }).always(function() {
          // 検索ボタンを有効にする
          $searchButton.attr('disabled', false);
        });
      }
      // サブミットしないので false を返す
      return false;
    };

    // 検索入力欄でEnterキーが押されたときの処理
    $('#searchForm').submit({navi:0}, doSearch);
    // 前ページリンクが押されたときの処理
    $('#result').on("click", "#prevPageLink", {navi:-1}, doSearch)
    // 次ページリンクが押されたときの処理
      .on("click", "#nextPageLink", {navi:1}, doSearch);
  });

baseUrlでFessサーバのURLを指定します。SERVERNAMEをFessサーバ名に書き換えてください。JSON形式で取得するので、サーバのURLの後にjson/?q=を指定します。

「fess.js」では検索処理関数doSearchを定義しています。検索フォームがサブミットされたとき、ページリンクがクリックされたときにdoSearchを呼び出します。

検索処理関数doSearch

ここからは検索処理関数doSearchについて説明します。doSearchでは検索リクエストの送信と、検索結果の表示をおこないます。

リクエストが正常に返ってくると、doneの関数が実行されます。 doneの引数には Fess サーバーから返却された検索結果のオブジェクトが渡されます。

検索結果はdata.response.resultに配列として格納されています。 results[i].〜でアクセスすることで検索結果ドキュメントのフィールド値を取得することができます。

実行

「index.html」にブラウザでアクセスすると、検索フォームが表示されます。

検索フォーム

適当な検索語を入力して、検索ボタンを押下すると検索結果が表示されます。 デフォルトの表示件数は20件ですが、ヒットした検索件数が多い場合には検索結果一覧の下に次のページへのリンクが表示されます。

検索結果

*  *  *

FessのJSON APIを利用してjQueryベースのクライアント検索サイトを構築してみました。 JSON APIを利用することでブラウザベースのアプリケーションに限らず、別のアプリケーションから呼び出してFessを利用するシステムも構築できます。

著者紹介

菅谷 信介 (Shinsuke Sugaya)

Apache PredictionIOにて、コミッター兼PMCとして活動。また、自身でもCodeLibs Projectを立ち上げ、オープンソースの全文検索サーバFessなどの開発に従事。

※ 本記事は掲載時点の情報であり、最新のものとは異なる場合がございます。予めご了承ください。

一覧はこちら

連載目次

もっと知りたい!こちらもオススメ

【連載】Kubernetes入門 [1] Kubernetesとは

【連載】Kubernetes入門 [1] Kubernetesとは

マイクロサービス化が進み、より細かい単位でコンテナを起動したり、高負荷時にはより細かい単位でコンテナをスケールさせたりできます。つまりコンテナが乱立し、運用管理が困難になってきているのです。そんな中注目されているのがコンテナオーケストレーションツール「Kubernetes」です。

関連リンク

この記事に興味を持ったら"いいね!"を Click
Facebook で IT Search+ の人気記事をお届けします

会員登録(無料)

注目の特集/連載
[解説動画] Googleアナリティクス分析&活用講座 - Webサイト改善の正しい考え方
[解説動画] 個人の業務効率化術 - 短時間集中はこうして作る
知りたい! カナコさん 皆で話そうAIのコト
教えてカナコさん! これならわかるAI入門
対話システムをつくろう! Python超入門
Kubernetes入門
AWSで作るクラウドネイティブアプリケーションの基本
PowerShell Core入門
徹底研究! ハイブリッドクラウド
マイナビニュース スペシャルセミナー 講演レポート/当日講演資料 まとめ
セキュリティアワード特設ページ

一覧はこちら

今注目のIT用語の意味を事典でチェック!

一覧はこちら

ページの先頭に戻る