[kintone]アプリ内検索アプリを作った

JavaScript

kintoneのアプリ内検索はどのようにやっていますか?

  • アプリを開いた後、右上の検索窓で検索(標準機能)
  • 一覧検索プラグインを使う

などがパッと出てくる方法じゃないかなーと思います。

アプリ内検索

ですが、私がkintone使い始めたころ、その2つとも知らなかったんですね。
(社内の誰も知らなかった…(;・∀・)
そんな中で、どうしてもアプリの中を検索したくて考えて作ったアプリがあります。

というわけで、今回は、今となっては微妙な立ち位置となった、アプリ内検索アプリについてお話します。

アプリの内容を検索するにはJavaScript必須

kintoneの標準機能の豊富さをまだ知らない&プラグインという便利なものを知らない頃、kintoneの機能拡張と言えば独自にJavaScriptを使うしかない!と思ってました。(無知って怖いね)
JavaScriptを使えばデータの検索&取得はできそう…
そのあとどうやって表示しようかな…と思ってたところで、こちらの記事を参考にすることにしました。

https://developer.cybozu.io/hc/ja/articles/360000528186-N-N-%E8%A4%87%E6%95%B0%E5%AF%BE%E8%A4%87%E6%95%B0-%E3%81%AE%E9%96%A2%E9%80%A3%E3%83%AC%E3%82%B3%E3%83%BC%E3%83%89%E4%B8%80%E8%A6%A7%E3%82%92%E8%87%AA%E4%BD%9C%E3%81%99%E3%82%8B

この記事にも書かれていますが、レコード内のフィールドの値で紐づけてアプリのデータを引っ張ってくることは関連レコードでできますが、今回やりたかったのが、

  • フィールドの項目ではなく、独自に検索ワードを指定して検索する
  • サブテーブルの内容も検索する

ということだったので、この記事を参考にすることにしました。

用意するフィールドと処理の流れ

まずは検索文字列を文字列フィールドで用意。
ついでにOR検索用の文字列フィールドも用意。(8個あるけど実際3つくらいまでしか使ってない)
検索結果はスペースフィールドにHTMLで書き込む形式なので、スペースフィールドを用意しておきます。

フィールド構成

やっていることをざっと説明しますと、

  1. 文字列フィールドから検索文字列を取得
  2. その文字列を含んだ検索用REST API文を生成
  3. 検索結果をfor文で回してHTMLの<TABLE>タグ内に配置

という形です。

ソースはほぼベタ書きで汚いので公開するか迷いましたが、せっかくご紹介するのであれば汚くても出しておくべきかなと思い公開します!(/ω\)キャー
(ああでもかなり恥ずかしい!真似するときは良い感じにキレイに作ってみてね!)

参考ソース

/* 
* 元ネタ:サンプルカスタマイズ
* N:N(複数対複数)の関連レコード一覧を自作する 
* Copyright (c) 2018 Cybozu 
* 
* Licensed under the MIT License 
*
* 問合せ一覧からキーワード指定で絞り込む
*/ 
(function() { 
    'use strict'; 
    kintone.events.on(['app.record.detail.show', 'app.record.edit.show'], function(event) { 
        var record = event.record;
        //console.log('処理スタート');
        // 増殖バグ回避 
        if (document.getElementById('REC_SPACE') !== null) { 
            return event; 
        } 
        // To HTML escape 
        function escapeHtml(str) { 
            if (str === '') {
              return '';
            } 
            
            if (str === null) {
              return '';
            }
          
            return str 
                .replace(/&/g, '&amp;') 
                .replace(/</g, '&lt;') 
                .replace(/>/g, '&gt;') 
                .replace(/"/g, '&quot;') 
                .replace(/'/g, '&#39;'); 
        } 
        // スペースを取得 
        var subtableSpace = kintone.app.record.getSpaceElement('REC_SPACE'); 
        
       
        // Rest API 
        // 問合せ内容 like "EXCEL" or 問合せ内容 like "excel" order by 日付_問合せ日 desc limit 100 offset 0
        
        // 類似キーワードがあればor条件でつなげる
        var keyword1 = '問合せ内容 like "' + record.キーワード.value;
          
        if (record.類似1.value) {
          keyword1 += '" or 問合せ内容 like "' + record.類似1.value;
        }
        if (record.類似2.value) {
          keyword1 += '" or 問合せ内容 like "' + record.類似2.value;
        }
        if (record.類似3.value) {
          keyword1 += '" or 問合せ内容 like "' + record.類似3.value;
        }
        if (record.類似4.value) {
          keyword1 += '" or 問合せ内容 like "' + record.類似4.value;
        }
        if (record.類似5.value) {
          keyword1 += '" or 問合せ内容 like "' + record.類似5.value;
        }
        if (record.類似6.value) {
          keyword1 += '" or 問合せ内容 like "' + record.類似6.value;
        }
        if (record.類似7.value) {
          keyword1 += '" or 問合せ内容 like "' + record.類似7.value;
        }
        if (record.類似8.value) {
          keyword1 += '" or 問合せ内容 like "' + record.類似8.value;
        }
        
        // 問合せ案件 問い合わせ内容検索
        var params = { 
            'app': zzz, 
            'query': keyword1 + '" order by 日付_問合せ日 desc limit 500'
        }; 
        
        //console.log('処理クエリ設定:',params);
        
        kintone.api(kintone.api.url('/k/v1/records', true), 'GET', params).then(function(resp) { 
            // success:問合せ一覧を表示する 
            var tableRecords = resp.records; 
            //console.log('tableRecords:', tableRecords);
            
            // 結果件数
            var kekkaCountSpace2 = '<div class="control-gaia control-label-field-gaia">検索結果件数<b>';
            kekkaCountSpace2 += tableRecords.length;
            kekkaCountSpace2 += '</b>件</div>';
            countSpace2.innerHTML = kekkaCountSpace2;
            
            var toiTable = '<table class="kintoneplugin-table" style="width: 1100px;">'; 
            toiTable += '<thead>'; 
            toiTable += '<tr>'; 
            toiTable += '<th class="kintoneplugin-table-th" style="width: 150px;">'; 
            toiTable += '<span class="title">'; 
            toiTable += '問合せ日'; 
            toiTable += '</span>'; 
            toiTable += '</th>'; 
            
            toiTable += '<th class="kintoneplugin-table-th" style="width: 200px;">'; 
            toiTable += '<span class="title">'; 
            toiTable += '問合せ分類'; 
            toiTable += '</span>'; 
            toiTable += '</th>'; 
            
            toiTable += '<th class="kintoneplugin-table-th" style="width: 500px;">'; 
            toiTable += '<span class="title">'; 
            toiTable += '問合せ内容'; 
            toiTable += '</span>'; 
            toiTable += '</th>'; 
            
            toiTable += '<th class="kintoneplugin-table-th" style="width: 250px;">'; 
            toiTable += '<span class="title">'; 
            toiTable += '会社'; 
            toiTable += '</span>'; 
            toiTable += '</th>';
            toiTable += '</tr>'; 
            toiTable += '</thead>'; 
            toiTable += '<tbody>'; 
            //console.log('toiTable1:',toiTable);
            for (var i = 0; i < tableRecords.length; i++) { 
                toiTable += '<tr>'; 
                toiTable += '<td>'; 
                toiTable += '<div class="kintoneplugin-table-td-control">'; 
                toiTable += '<a href="/k/zzz/show#record=' + escapeHtml(tableRecords[i].$id.value); 
                toiTable += '" target="_blank">'; 
                toiTable += escapeHtml(tableRecords[i].日付_問合せ日.value); 
                toiTable += '</a>'; 
                toiTable += '</div>'; 
                toiTable += '</td>'; 
                toiTable += '<td>'; 
                toiTable += '<div class="kintoneplugin-table-td-control">'; 
                toiTable += escapeHtml(tableRecords[i].問合せ分類.value); 
                toiTable += '</div>'; 
                toiTable += '</td>'; 
                toiTable += '<td>'; 
                toiTable += '<div class="kintoneplugin-table-td-control">'; 
                toiTable += escapeHtml(tableRecords[i].問合せ内容.value); 
                toiTable += '</div>'; 
                toiTable += '</td>'; 
                toiTable += '<td>'; 
                toiTable += '<div class="kintoneplugin-table-td-control">'; 
                toiTable += escapeHtml(tableRecords[i].会社.value); 
                toiTable += '</div>'; 
                toiTable += '</td>'; 
                toiTable += '</tr>'; 
            } 
            toiTable += '</tbody>'; 
            toiTable += '</table>'; 
           
            subtableSpace.innerHTML = toiTable; 
            
        }, function(error) { 
            console.log('Err:', error);
            // error:エラーの場合はメッセージを表示する 
            var errmsg = 'レコード取得時にエラーが発生しました。'; 
            // レスポンスにエラーメッセージが含まれる場合はメッセージを表示する 
            if (typeof error.message !== 'undefined') { 
                errmsg += '\n' + error.message; 
            } 
            subtableSpace.appendChild(document.createTextNode(errmsg)); 
        }); 
 
})(); 

実際はサブテーブル内文字列検索もあるのですが、そこまでソースに入れるとかなりごちゃついたので、今回は単純な文字列複数行フィールドの検索の部分だけ抜き出しで公開することにしました。(抜き出しミスってたらすみません…)
zzzはアプリ番号のことです。

実際のアプリイメージ

レコード登録時

レコード登録時

登録した結果

OR検索の結果が出ます

標準アプリ内検索との違い

お手軽さでいうと、断然、標準のアプリ内検索窓からの検索が上です。なのですが、アプリ内検索窓からの検索の場合、ヒットしたとしても全文が表示されるわけではなく、また表示したい項目も選べません。
今回紹介したアプリ内検索アプリの場合は、自分が好きな項目をテーブル形式で表示できるので一覧性はこちらの方が上です。

あとは、アプリ内検索アプリの場合は、検索文字列をずっと保存できる点が強みです。良く検索するワードを保存しておけるので、事例検索には持ってこいかなと思います。

以前検索した文字列が保存されています

OR検索ができるので、Excelとエクセルといった表記ブレも同時に検索がかけられる点もメリットとなります。
最初に必要だったからOR検索にしましたが、今はAND検索が欲しくなっているので、おいおい機能拡張をしたいなと思っています。

まとめ

今回紹介した方法は、ゴリゴリにJavaScriptを組んでいるので、拡張性やメンテナンス性についてはいまいちなところもありますが、こういう方法もあるよ~てな感じで参考にしていただければと思います。うまく作ればプラグイン化できそうな気もしますね。ネタとして温めておきます。

※うまくまとめきれなかったので、後日修正するかもしれません…

タイトルとURLをコピーしました