e-learning、オラクル研修、LMS(学習管理システム)のiStudy

e-learning、オラクル研修、LMS(学習管理システム)のiStudy

第129回「実用WEBアプリ 文書管理システム(10)検索機能(その1 検索条件入力フォーム)」

2015.05.21

こんにちは。インストラクターの蓑島です。

前回までで、文書やディレクトリの登録、表示、更新などの機能が揃いました。いわば、コアな機能が完成したわけですね。今回からは、利便性を向上する観点からその他の機能を加えていきたいと思います。

その第一弾として、検索機能を取り上げました。検索機能は重要ですね。キーワードを指定してそれを含む文章やディレクトリを検索できれば大変便利です。

今回から3回にわたって、以下のような順番で同じプロシージャに対して、検索機能を順次実装していこうと考えています。

1回目  検索条件入力フォームを作成する
2回目  その入力フォームの検索条件をもとに、SELET文を生成する(動的SQL)
3回目  そのSELECT文を実行し、結果を一覧表示する (カーソル変数を使った動的SQLの明示カーソル処理)

実装する検索機能は以下の3つの要件を満たすようにしたいと考えています。

1. キーワードは2つまで指定できる。2つ指定する場合は、AND条件でもOR条件でも可能。
2. 「タイトル」を検索対象とする。あるいは、「タイトルと本文」を検索対象とする。
3. 特定のディレクトリ以下を検索する。あるいはすべてのディレクトリ以下を検索する。

要約すれば、「タイトル」または「タイトルと本文」に指定したキーワード(2つまで指定可能)を含む文書やディレクトリを検索できるが、その際、特定のディレクトリ以下を対象とすることもできるし、すべてのディレクトリを対象にすることもできる、ということです。

上記3つの要件に対応するために以下の5つのパラメータを使う予定です。

・ディレクトリ番号  指定すれば、そのディレクトリ以下を検索、指定しなければすべてのディレクトリを対象
・区分        「タイトル」を検索するのか、「タイトルと本文」を検索するのかの指定
・キーワード1    指定するキーワード1
・キーワード2    指定するキーワード2
・条件        キーワードが2つの場合に意味がある。AND条件なのか、OR条件なのかの指定

したがって、今回作成する検索条件の入力フォーム画面は、上記5つをパラメータとして含むわけです。

そして今回の入力フォーム画面は、いままでのパターンとは違った大きな特徴を持たせる予定です。その特徴とは、パラメータの送信先であるFORMタグのACTION属性が、そのフォーム画面を生成するプロシージャと同じであるということです。

今までのパターンですと、入力フォーム画面を生成するプロシージャと、実際に処理を行うプロシージャ(FORMタグのACTION属性のプロシージャ)は常に違うプロシージャだったのですが、今回は同じプロシージャにしましょう。

なぜ入力フォームのプロシージャと処理を実行するプロシージャを同じにする理由は、条件を変えての再検索を簡単にできるようにするためです。

今までのパターンでは、検索条件の入力フォーム画面があり、送信ボタンを押すと、別プロシージャに送信され処理を実行し、別画面で結果を表示します。しかし、そのパターンでは条件を変えて再検索を行う場合、検索条件の入力フォーム画面に戻り、条件値を変えて同じことを繰り返さなければなりません。ですが、送信先の実行プロシージャが、入力フォーム画面とおなじプロシージャならば、同じ入力フォーム画面で、検索結果の一覧表示もできます。したがって、同じ画面上で、条件値を変えての再検索が簡単にできるわけです。

では、早速、以下のソースコードをご覧ください。プロシージャの名前をDOC_FINDとしました。今回の段階では入力フォームとしての外観だけを実装しています。具体的な検索のための記述は次回以降に行います。

(注意)以下のソースコードはブラウザ表示のために、山括弧(「<」と「>」)を全角にしています。 コピーして実行する方は、必ず、すべての山括弧(「<」と「>」)を半角に変換してください。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
CREATE OR REPLACE PROCEDURE DOC_FIND
( P_OYA_DIR   IN  VARCHAR2 DEFAULT NULL ,   -- 親ディレクトリ
   P_KEYWORD1  IN  VARCHAR2 DEFAULT NULL ,   -- キーワード1
   P_KEYWORD2  IN  VARCHAR2 DEFAULT NULL ,   -- キーワード2
   P_AND_OR    IN  VARCHAR2 DEFAULT 'AND' -- 「AND」または「OR」
   P_COL       IN  VARCHAR2 DEFAULT 'BOTH' , -- 「タイトル」または「タイトルと本文」
   P_BUTTON    IN  VARCHAR2 DEFAULT  NULL -- 送信ボタンをクリックしたかどうかの判定
IS
   V_OYA_DIR   NUMBER;
   V_ERRM      VARCHAR2(1000);
   V_KBN       DOCS.KBN%TYPE;
   V_SELECT_KBN_LIST  VARCHAR2(300);
   V_AND_OR_LIST      VARCHAR2(300);
BEGIN
/**********************************/
/*  検索条件の入力フォームを生成  */
/**********************************/
   -- 検索対象区分のリストを生成
   V_SELECT_KBN_LIST := '<SELECT NAME="P_COL"><OPTION VALUE="TITLE">タイトルのみ</OPTION>' ||
                        '<OPTION VALUE="BOTH">タイトルと本文</OPTION></SELECT>' ;
   V_SELECT_KBN_LIST := REPLACE (V_SELECT_KBN_LIST, '"' || P_COL || '"' , '"' ||P_COL|| '" SELECTED' );
   -- AND_OR のリストを作成
   V_AND_OR_LIST := '<SELECT NAME="P_AND_OR"><OPTION>AND</OPTION><OPTION>OR</OPTION></SELECT>' ;
   V_AND_OR_LIST := REPLACE (V_AND_OR_LIST, '<OPTION>' ||P_AND_OR, '<OPTION SELECTED>' ||P_AND_OR);
   -- 検索条件の入力フォームを作成
   HTP.P( '<HTML>' );
   HTP.P( '<HEAD><TITLE>ディレクトリ・文書検索</TITLE></HEAD>' );
   HTP.P( '<BODY>' );
   HTP.P( '<H2>ディレクトリ・文書検索</H2>' );
   HTP.P( '<FORM  ACTION="doc_find" METHOD="POST">' );
   HTP.P( '<TABLE BORDER>' );
   HTP.P( '<TR><TD>親ディレクトリ(省略時:全ディレクトリ対象)</TD>' ||
             '<TD><INPUT TYPE="TEXT" NAME="P_OYA_DIR" VALUE="' ||P_OYA_DIR|| '"></TR>' );
   HTP.P( '<TR><TD>区分</TD><TD>' || V_SELECT_KBN_LIST || '</TD></TR>' );
   HTP.P( '<TR><TD>キーワード1</TD>' ||
         '<TD><INPUT TYPE="TEXT" NAME="P_KEYWORD1" VALUE="' || P_KEYWORD1 || '"></TD></TR>' );
   HTP.P( '<TR><TD>キーワード2</TD>' ||
         '<TD><INPUT TYPE="TEXT" NAME="P_KEYWORD2" VALUE="' || P_KEYWORD2 || '"></TD></TR>' );
   HTP.P( '<TR><TD>条件</TD><TD>' ||V_AND_OR_LIST || '</TD></TR>' );
   HTP.P( '</TABLE>' );
   HTP.P( '<INPUT TYPE="SUBMIT" NAME="P_BUTTON" VALUE="検索実行">' );
   HTP.P( '</FORM>' );
/**********************************************/
/*  以下は検索実行ボタンが押された場合の処理  */
/**********************************************/
   IF P_BUTTON = '検索実行' THEN
      HTP.P( '検索実行ボタンが押されてこの画面が開いています' );  -- DEBUG
   ELSE
      HTP.P( 'URLからこのプロシージャを指定して開いています' );    -- DEBUG
   END IF;
   -- HTMLページの最後
   HTP.P( '</BODY>' );
   HTP.P( '</HTML>' );
EXCEPTION
   WHEN OTHERS  THEN
       V_ERRM    := SQLERRM;
       HTP.P(V_ERRM);
END DOC_FIND;
/
 
プロシージャが作成されました。

では操作の流れに沿って解説します。

初回にこのプロシージャがコールされるときというのは、ブラウザなどで、URLを指定されるケースですね。
何を言いたいのかというと、このプロシージャにより生成されるフォーム画面の送信先(FORMタグのACTION属性 30行目)も、このプロシージャですから、そのようなFORMタグのACTION属性でこのプロシージャがコールされるケースもあります。しかしその場合、先にそのフォーム画面がなければいけません。
ですから、少なくとも、初回はフォーム画面を必要とせずに呼びすケースだということです。それはつまり、ブラウザや、HTMLのリンクなどで、URLを直接指定してコールされるケースですね。そうすると、フォーム画面に検索条件をセットする以前の段階ですから、検索条件などのリクエストパラメータは存在しません。
ですから、このプロシージャのパラメータは初回時など存在しない場合がありえるので、デフォルト値が必要です(2~7行目)

そしてこれら初回のデフォルト値のパラメータが、フォームの各項目の値として、フォーム画面にセットされる訳です。例えば、33,36,38行目のVALUE属性の値です。あるいはデフォルト値がNULLでないパラメータ(5行目の P_AND_ORや、6行目のP_COL)は、具体的には、画面上は選択リストボックス(SELECTタグ 19~20行目、23行目)であり、指定されたそれらパラメータでもって、該当するOPTIONが選択された状態(SELECTED)にセットされます(21行目や24行目のREPLACE処理)。
選択リストボックスの該当オプションを選択済み(SELECTED)状態にするためのREPLACE処理がわかりにくいと思いますが、「バックナンバー第110回「WEBアプリ作成(8)データ更新(UPDATE)(1/2)」でその手法を詳しく解説していますので、興味のある方はご覧になってください。

いずれにしろ、初回時オープンしたフォーム画面の項目はNULLだったり、選択リストボックスはデフォルト値が選択された状態であり、いわば空のフォームの状態ですね。

では、このような空のフォームに対して具体的な検索条件をセットし、41行目にある検索実行ボタンをクリックすると、FORMタグの範囲(30~41行目)のリクエストパラメータが、プロシージャに送信される訳ですね。そして、このときは初回時と違って、各パラメータに具体的な値がある。特に、検索実行ボタン自身がP_BUTTONという名前(NAME="P_BUTTON")のリクエストパラメータになっており、その値(VALUE="検索実行")である"検索実行"という文字が送信されるわけです。
ですから、P_BUTTONの値が'検索実行'という値であることを判定(46行目のIF文 「IF P_BUTTON = '検索実行' THEN」)することにより、明確に検索実行ボタンをクリックしてコールされたのかどうかを判断できるわけです。検索ボタンが押されてこのプロシージャがコールされたと判断された場合は、47行目でデバッグ的なメッセージ(「検索実行ボタンが押されてこの画面が開いています」)を表示しています。次回以降はこのプロシージャに検索実行処理(SELECT文を生成し、それを実行し、結果を表示する)を追加するわけですが、その処理の記述位置が、この47行目に相当する位置であることがお判りいただけると思います。

では、実際にこのプロシージャをコールしてみましょう。初回はブラウザのURLから直接指定して実行するわけですね。

バックナンバー 第103回「WEBアプリ作成(1) (Oracle DBとPL/SQLだけで、即、WEBアプリ)」と同等の設定ができていれば、以下のURLです。

http://localhost:8080/dad/doc_find

ログインを求められたら、スキーマユーザ(私の場合は scott)でログインしてください。

URLを指定してこのプロシージャを呼び出しました。画面下部のメッセージ「URLからこのプロシージャを指定して開いています」が表示されていますね。フォームの項目は空であり、選択リストはデフォルト値です。つまり空のフォームですね。

フォームの項目に値をセットします(「abc」、「xyz」)。また選択リストの値もデフォルト以外に変更します(「タイトルと本文→タイトルのみ」、「AND→OR」)。その状態で、検索実行ボタンをクリックします。それにより、画面の項目がプロシージャへ送信されます。

プロシージャがリクエストパラメータを受信して画面を再生成しました。画面の項目が前の状態からそのまま引き継がれていることがわかります。画面下部の「検索実行ボタンが押されてこの画面が開いています」というメッセージ表示があるということは、46行目のIF文(IF P_BUTTON = '検索実行' THEN)がTRUEであったことを示しています。つまりプログラム内で、検索実行ボタンが押されたことにより、プロシージャがこのフォーム画面を生成したことがわかります。
この位置に次回以降の処理(SELECT文の生成やその実行、結果表示など)を実装していけば、このメッセージ表示の代わりに、検索を実行した結果を表示できるわけです。

ということで、今回は検索条件を指定するフォーム画面を作成しました。具体的な検索処理は実装していないので、いわば外枠だけつくったようなものです。
次回以降はこの中に検索処理の中味を実装していきましょう。次回は冒頭で申し上げたように、検索条件をもとにSELECT文を生成する処理を実装します。
そしてさらに次の回で、そのSELECT文を実行した結果を、検索の結果として画面に表示します。

それでは次回、ご期待ください。

先頭へ戻る