本ブログではGistにコード・スニペットを保存して、記事に貼り付けていることが多いのですが、コード・スニペットを対象とした検索ができなくて困っていました。
APEX 22.2では検索コンポーネントが新規に追加されています。これを使ってコード・スニペットを検索するアプリケーションを作ってみました。
https://apex.oracle.com/pls/apex/r/japancommunity/codesearch/home
検索コンポーネントについては、Oracle APEXのOffice Hourで開発者のCarsten Czarskiさんが解説しています。
Part 2: Marquee Features (APEX 22.2)
YouTubeの動画では、22分頃から解説が始まります。
https://www.youtube.com/watch?v=EEYLgNxx3Wo&t=1320s
今回のアプリケーションですが、クイックSQLの以下のモデルを使って表を作成します。
#prefix: cds
documents
title vc4000 /nn
url vc400
content clob
published date
updated date
attachments
post_id /fk documents
embedded_url vc400
embedded_content clob
raw_url vc400
raw_content clob
embedded_updated date
raw_updated date
ブログ記事の本文を保存する表CDS_DOCUMENTSに対して、複数のコード(添付ファイル)が表CDS_ATTACHMENTSとして紐づくという構造になっています。一般的によくある構造なので、応用はしやすいと思います。
以下より、アプリケーションを作成する手順を紹介します。
最初に上記のクイックSQLのモデルより、表CDS_DOCUMENTSとCDS_ATTACHMENTSを作成します。
SQLワークショップのユーティリティのクイックSQLを開き、左ペインにモデルを貼り付けます。その後SQLの生成、SQLスクリプトを保存、レビューおよび実行を順次クリックします。
表CDS_DOCUMENTSの列IDの型定義は以下に変更します。主キー制約の定義は変更しません。
参照制約が定義されているCDS_ATTACHMENTSの列POST_IDの型はvarchar2(32)に変更します。
以上の変更を行い、スクリプトを実行します。
アプリケーション作成ウィザードを、アプリケーション・ビルダーから起動します。
アプリケーションの名前はコード検索とします。テストに使用するデータを投入するための画面を、あらかじめアプリケーションに作成します。本題の検索画面は、アプリケーションを作成したのちに作成します。
ページの追加をクリックします。
ページの追加の追加ページを開き、複数のレポートを選択します。
表の選択画面が開きます。表CDS_DOCUMENTS、CDS_ATTACHMENTSを探してチェックを入れます。
ページの追加をクリックします。
それぞれの表について、フォーム付きの対話モード・レポートが追加されます。編集をクリックし、管理ページに切り替えます。
管理ページに変更すると、編集ボタンの横にあるアイコンがスパナに変わります。
AttachmentsとDocumentsの双方を管理ページに変更し、アプリケーションの作成を実行します。
以上でアプリケーションが作成されます。
アプリケーションを実行し管理画面を開くと、表CDS_DOCUMENTSとCDS_ATTACHMENTSの編集メニューが含まれていることが確認できます。テストに使うデータはこちらから登録できます。
apex.oracle.com上のアプリケーションではこの画面を使わずに、ブログ記事本文を表CDS_DOCUMENTSの列CONTENTに、Gistのスニペットを表CDS_ATTACHMENTSのRAW_CONTENTに投入しています。
表CDS_DOCUMENTSの列CONTENTおよびCDS_ATTACHMENTSの列RAW_CONTENTにOracle Textの全文検索索引を作成します。列CONTENTはレクサーとしてJAPANESE_LEXER、列RAW_CONTENTはコードなのでBASIC_LEXERを使用します。
あらかじめお断りしておきますが、最終的にOracle Text検索は使用しません。コード中のプロシージャ名やファンクション名の検索に向いていないためです。Oracle Textについては、一つの実装例としての紹介になります。
それぞれのレクサーのプレファレンスを、ja_lexerおよびbasic_lexerとして作成します。
begin
ctx_ddl.create_preference('ja_lexer', 'JAPANESE_LEXER');
ctx_ddl.create_preference('basic_lexer', 'BASIC_LEXER');
end;
/
表CDS_DOCUMENTSの列CONTENTに、Oracle Text索引CDS_DOCUMENTS_CTX1を作成します。
create index cds_documents_ctx1 on cds_documents(content)
indextype is ctxsys.context parameters('filter ctxsys.null_filter lexer ja_lexer sync(on commit)');
同様に表CDS_ATTACHMENTSの列RAW_CONTENTに、索引CDS_ATTACHMENTS_CTX1を作成します。
create index cds_attachments_ctx1 on cds_attachments(raw_content)
indextype is ctxsys.context parameters('filter ctxsys.null_filter lexer basic_lexer sync(on commit)');
以上で検索画面を作成する準備ができました。
共有コンポーネントの構成の検索を開きます。(英語だとSearch Configurationsで、これ以外は検索構成と訳されています。)
作成済みの検索構成が一覧されます。作成をクリックします。
検索構成の名前は本文検索 - Oracle Textとします。検索タイプとしてOracle Textを選択します。
次へ進みます。
ソースの表/ビューの名前として、CDS_DOCUMENTSを選択します。
次へ進みます。
主キー列はID(Varchar2)、Oracle Text索引列はCONTEXT(Clob)、タイトル列はTITLE(Varchar2)とします。アイコン・ソースとしてアイコン・クラスを選択し、アイコンCSSクラスとしてfa-file-text-oを指定します。
検索構成の作成をクリックします。
検索構成本文検索 - Oracle Textが作成されます。
設定の検索問合せ接頭辞にcontent-text、静的IDにcontent-textと設定します。列のマッピングのカスタム列1にURL(Varchar2)を選択します。
以上で変更の適用をクリックします。
名前はコード検索 - Oracle Textとします。検索タイプにOracle Textを選択します。
親となる表CDS_DOCUMENTSの列を検索結果に含めるため、ソースのソース・タイプとしてSQL問合せを選択し、以下のSQLをSQL SELECT文を入力に記述します。
select a.id, d.title, d.url, a.raw_url, a.raw_content
from cds_documents d join cds_attachments a
on d.id = a.post_id
主キー列はID(Number)、Oracle Text索引列はRAW_CONTENT(Clob)、タイトル列はTITLE(Varchar2)とします。アイコン・ソースとしてアイコン・クラスを選択し、アイコンCSSクラスとしてfa-file-text-oを指定します。
検索構成コード検索 - Oracle Textが作成されます。
設定の検索問合せ接頭辞にcode-text、静的IDにcode-textと設定します。列のマッピングのカスタム列1にURL(Varchar2)、カスタム列2にRAW_URL(Varchar2)を選択します。
以上で変更の適用をクリックします。
検索ページを作成します。このページをホームとするため、作成済みのホーム・ページを削除します。
ページ・デザイナでホーム・ページを開き、ページの削除を実行します。
ページが削除されたら、代わりになる検索ページを作成します。ページの作成を実行します。
検索ページを選択し、次へ進みます。
ページ番号は1、名前はコード検索とします。ページ・モードは標準です。
構成の検索としてコード検索 - Oracle Text、本文検索 - Oracle Textの双方をチェックします。
以上でページの作成をクリックします。
検索ページが作成されます。
今回はすべて公開されているデータを元にしているため、作成した検索ページの保護をすべて解除します。
識別の別名をhomeに変更します。
セキュリティの認証はパブリック・ページ、ディープ・リンクは有効、ページ・アクセス保護は制限なしとします。セッション管理のセッションを再結合はパブリック・セッションに対して有効に変更します。
変更を保存します。
検索ワードとしてapex_data_exportを入力します。
一件もヒットしません。
検索構成を変更するまえに、2つページの設定を変更します。
リージョン検索結果を選択し、プロパティ・エディタの属性を開きます。
デフォルトでは文字入力の都度、検索が実行されるようになっています。データベースへの負荷を減らすため、設定の入力時に検索をOFFに変更します。
続いて、検索結果として表示されるURLをクリックできるようにします。
設定のカスタム・レイアウトをONにし、結果行テンプレートに以下を書き込みます。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div class="a-ResultsItem &RESULT_CSS_CLASSES!ATTR."> | |
{if ?ICON_VALUE/} | |
{case ICON_TYPE/} | |
{when INITIALS/} | |
<div class="a-ResultItem-initials u-color-var">&ICON_VALUE.</div> | |
{when URL/} | |
<div class="a-ResultItem-image"><img src="&ICON_VALUE!ATTR." alt="&TITLE!ATTR." role="presentation" /></div> | |
{when CLASS/} | |
<div class="a-ResultItem-icon u-color-var"><span class="fa &ICON_VALUE!ATTR." aria-hidden="true"></span></div> | |
{endcase/} | |
{endif/} | |
<div class="a-ResultsItem-content"> | |
<div class="a-ResultsItem-header"> | |
{if ?LINK/} | |
<span class="a-ResultsItem-title"><a href="&LINK!ATTR.">&TITLE.</a></span> | |
{else/} | |
<span class="a-ResultsItem-title">&TITLE.</span> | |
{endif/} | |
{if ?BADGE/}<span class="a-ResultsItem-badge">&BADGE.</span>{endif/} | |
</div> | |
{if ?SUBTITLE/}<div class="a-ResultsItem-subTitle">&SUBTITLE.</div>{endif/} | |
{if ?DESCRIPTION/}<div class="a-ResultsItem-description">&DESCRIPTION.</div>{endif/} | |
{if ?HAS_CUSTOM_ATTRS/} | |
<div class="a-ResultsItem-attributes"> | |
{if ?CUSTOM_01/}<a href="&CUSTOM_01." target="_blank">&CUSTOM_01.</a>{endif/} | |
{if ?CUSTOM_02/}<a href="&CUSTOM_02." target="_blank">&CUSTOM_02.</a>{endif/} | |
{if ?CUSTOM_03/}<span class="a-ResultsItem-attribute">&CUSTOM_03.</span>{endif/} | |
</div> | |
{endif/} | |
{if ?LAST_MODIFIED/}<div class="a-ResultsItem-misc">&LAST_MODIFIED.</div>{endif/} | |
</div> | |
</div> |
カスタム列1の置換文字列は&CUSTOM_01.、カスタム列2は&CUSTOM_02.です。これらのカスタム列が現れる部分をA要素に変更しています。
結果行テンプレートの記述方法は、結果行テンプレートのオンライン・ヘルプに記載されています。
以上の変更で、検索結果として表示されるURLがクリック可能になります。テンプレート・ディレクティブの使用もできるため、検索結果の見栄えの自由度は高いでしょう。
Oracle Textの検索はコード検索の要件に合わなかったため、検索構成を新規に作成します。
新たに作成する検索構成の名前は本文検索 - 標準とします。検索タイプは標準を選択します。これ以降の指定は、検索タイプがOracle Textのときと同じです。
列のマッピングのカスタム列1としてURL(Varchar2)を選択します。
同様に検索構成コード検索 - 標準を作成します。
作成された検索構成コード検索 - 標準の設定の検索問合せ接頭辞、静的IDはcodeとします。ソースの検索可能列としてRAW_CONTENT(Clob)を選択します。
列のマッピングのカスタム列1としてURL(Varchar2)を選択します。カスタム列2としてRAW_URL(Varchar2)を選択します。
以上で、置き換える検索構成が作成されました。
検索ページを開き、ソースの検索のコード検索 - Oracle Textを選択します。
識別の名前をコード検索、検索構成をコード検索 - 標準に変更します。
同様に本文検索 - Oracle Textの識別の名前を本文検索、検索構成を本文検索 - 標準に変更します。
以上でアプリケーションは完成です。
アプリケーションを実行して、apex_data_exportで検索してみます。
記事およびGistのスニペットの内容にapex_data_export(大文字小文字は無視されます)が含まれる記事が一覧されます。
検索構成に検索問合せ接頭辞が設定されています。本文検索 - 標準にはcontentが指定されているため、検索キーワードとしてcontent:apex_data_exportと入力するとブログ記事本文のみが検索対象になります。
この他にもいろいろな機能が、最初に紹介したYouTubeの動画で紹介されています。
今回作成したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/codesearch.zip
Oracle APEXのアプリケーション作成の参考になれば幸いです。
完