この付録では、独自のコードを記述するか、またはOracle Textウィザードを使用して、CONTEXT索引タイプを使用した簡単なWeb検索アプリケーションを作成する方法を説明します。次の項目について説明します。
Oracle Textの一般的な使用目的は、Webサイト上のHTMLファイルを索引付けし、ユーザーに検索機能を提供することです。この付録のサンプル・アプリケーションでは、データベースに格納されているHTMLファイルのセットを索引付けし、Oracle Databaseに接続しているWebサーバーを使用して検索サービスを提供します。
この付録では、Web問合せアプリケーションの次の2つのバージョンについて説明します。
PL/SQL Server Pages(PSP)を使用するバージョン
Java Server Pages(JSP)を使用するバージョン
これらのアプリケーションのバージョンは両方とも、必要なコードを自動的に作成する問合せアプリケーション・ウィザードを使用して作成できます。
PSP、JSP両方のアプリケーション・コードおよびテキスト問合せアプリケーション・ウィザードは、次のOracle Technology NetworkのWebサイトで表示およびダウンロードできます。
http://www.oracle.com/technology/products/text
テキスト問合せアプリケーション・ウィザードのWebページには、このウィザードの使用方法の詳細な説明もあります。
図A-1に、テキスト問合せアプリケーションのJSPバージョンはどのようなものかを示します。このアプリケーションは、Oracle Textアプリケーション・ウィザードを使用して作成されています。
図A-2に、テキスト問合せの結果を示します。
このアプリケーションでは、検索用語を含むドキュメントへのリンクを戻します。各ドキュメントには、次の4つのリンクがあります。
「HTML」リンクによりドキュメントが表示されます。
フィルタ処理されたドキュメントでは、図版は表示されません。(図5-1「ハイライト表示、要旨作成およびテーマ抽出のサンプル・ドキュメント」に、最初にヒットしたドキュメントのソースを示しています。)
「ハイライト」リンクにより、検索用語をハイライト表示したドキュメントが表示されます。図5-2「petがハイライト表示されたPet Magnetドキュメント」に、ハイライトの例を示しています。
「テーマ」リンクにより、ドキュメントに関連する上位50位までのテーマが表示されます。図5-3「ドキュメントのテーマを表示する問合せアプリケーション」に、テーマの抽出例を示しています。
「要旨」リンクにより、ドキュメントの簡単な要約が表示されます。図5-4「ドキュメントの要旨を表示する問合せアプリケーション」に、この要旨表示機能の例を示しています。
このアプリケーションは、PL/SQL Server Pagesに基づいています。図A-3「PSP Webアプリケーション」は、ブラウザが、Webサーバーを経由してOracle DatabaseのPSPストアド・プロシージャをコールする方法を示しています。
このアプリケーションには、次の要件があります。
Oracle Database(リリース8.1.6以上)が起動され、実行中であること。
Oracle PL/SQL Gatewayが実行中であること。
ApacheなどのWebサーバーが起動され、実行中であり、Oracle Databaseに要求を送信するように適切に構成されていること。
この項では、PSP Webアプリケーションの作成方法を説明します。
HTMLファイルを格納するテキスト表を作成する必要があります。この例では、次のようにsearch_tableという表を作成します。
create table search_table (tk numeric primary key, title varchar2(2000), text clob);
テキスト表は、HTMLファイルを使用してロードする必要があります。この例では、制御ファイルloader.ctlを使用して、loader.datで指定したファイルをロードします。SQL*Loader文は次のとおりです。
% sqlldr userid=scott/tiger control=loader.ctl
テキスト問合せウィザードを使用している場合: このウィザードにより、索引を作成するスクリプトが作成されます。(ダウンロードしたWebページの、ウィザードについての説明を参照してください。)そのスクリプトを実行します。
ウィザードを使用していない場合: HTMLファイルを索引付けするには、次のようにCONTEXT索引をテキスト列に作成します。HTMLの索引付けであるため、この例では、フィルタ処理が不要なNULL_FILTERプリファレンス型とHTML_SECTION_GROUP型を使用します。
create index idx_search_table on search_table(text)
indextype is ctxsys.context parameters
('filter ctxsys.null_filter section group CTXSYS.HTML_SECTION_GROUP');
アプリケーションには、選択したドキュメントが表示される必要があります。このために、Oracle Databaseではsearch_tableのCLOBからドキュメントを読み取り、その結果を出力して表示する必要があります。これは、search_htmlservicesパッケージ内のプロシージャをコールして実行します。ファイルsearch_htmlservices.sqlをコンパイルする必要があります。この操作は、SQL*Plusプロンプトで実行できます。
SQL> @search_htmlservices.sql Package created.
検索ページを呼び出すには、ブラウザからsearch_html.pspをコールします。search_htmlは、Oracle Databaseのloadpspコマンドライン・プログラムを使用して、次のようにコンパイルします。
% loadpsp -replace -user scott/tiger search_html.psp "search_html.psp": procedure "search_html" created.
|
関連項目 PSPの使用方法の詳細は、『Oracle Databaseアドバンスト・アプリケーション開発者ガイド』を参照してください。 |
クライアントのPSP要求をURLとして受け入れるように、Webサーバーを構成する必要があります。Webサーバーは、クライアントの要求をOracle Databaseに転送し、サーバー出力をブラウザに戻します。図A-3を参照してください。
Oracle WebDB Webリスナー、またはApache Webサーバーが組み込まれているOracle Application Serverを使用できます。詳細は、使用しているWebサーバーのマニュアルを参照してください。
URLを使用して、ブラウザから問合せアプリケーションにアクセスできます。URLをWebサーバーで構成します。URLの例は、次のようになります。
http://server.example.com:7777/mypath/search_html
図A-1および図A-2で示すように、アプリケーションでは、ブラウザに問合せのエントリ・ボックスを表示し、問合せ結果をHTMLリンクのリストとして戻します。
この項では、Webアプリケーションのサンプルの作成に使用するコードを示します。次のファイルを記載しています。
この例では、loader.ctlファイルのサンプルを示します。sqlldrでは、このファイルを使用してデータファイルloader.datがロードされます。
LOAD DATA
INFILE 'loader.dat'
INTO TABLE search_table
REPLACE
FIELDS TERMINATED BY ';'
(tk INTEGER,
title CHAR,
text_file FILLER CHAR,
text LOBFILE(text_file) TERMINATED BY EOF)
この例では、loader.datファイルのサンプルを示します。各行は、3つのフィールド(ドキュメントの参照番号、ラベル(すなわちタイトル)、およびsearch_tableのテキスト列にロードされるHTMLドキュメント名)で構成されます。この例では、ファイルは切り捨てられています。
1; Pizza Shredder;Pizza.html
2; Refrigerator w/ Front-Door Auto Cantaloupe Dispenser;Cantaloupe.html
3; Self-Tipping Couch;Couch.html
4; Home Air Dirtier;Mess.html
5; Set of Pet Magnets;Pet.html
6; Esteem-Building Talking Pillow;Snooze.html
. . .
28; Shaggy Found Inspiration For Success In Jamaica ;shaggy_found.html
29; Solar Flare Eruptions Likely ;solar_flare.html
30; Supersonic Plane Breaks Food Barrier ;food_barrier.html
31; SOUNDSCAN REPORT: Recipe for An Aspiring Top Ten;urban_groove_1.html
. . .
set define off
create or replace package search_htmlServices as
procedure showHTMLDoc (p_id in numeric);
procedure showDoc (p_id in varchar2, p_query in varchar2);
end;
/
show errors;
create or replace package body search_htmlServices as
procedure showHTMLDoc (p_id in numeric) is
v_clob_selected CLOB;
v_read_amount integer;
v_read_offset integer;
v_buffer varchar2(32767);
begin
select text into v_clob_selected from search_table where tk = p_id;
v_read_amount := 32767;
v_read_offset := 1;
begin
loop
dbms_lob.read(v_clob_selected,v_read_amount,v_read_offset,v_buffer);
htp.print(v_buffer);
v_read_offset := v_read_offset + v_read_amount;
v_read_amount := 32767;
end loop;
exception
when no_data_found then
null;
end;
end showHTMLDoc;
procedure showDoc (p_id in varchar2, p_query in varchar2) is
v_clob_selected CLOB;
v_read_amount integer;
v_read_offset integer;
v_buffer varchar2(32767);
v_query varchar(2000);
v_cursor integer;
begin
htp.p('<html><title>HTML version with highlighted terms</title>');
htp.p('<body bgcolor="#ffffff">');
htp.p('<b>HTML version with highlighted terms</b>');
begin
ctx_doc.markup (index_name => 'idx_search_table',
textkey => p_id,
text_query => p_query,
restab => v_clob_selected,
starttag => '<i><font color=red>',
endtag => '</font></i>');
v_read_amount := 32767;
v_read_offset := 1;
begin
loop
dbms_lob.read(v_clob_selected,v_read_amount,v_read_offset,v_buffer);
htp.print(v_buffer);
v_read_offset := v_read_offset + v_read_amount;
v_read_amount := 32767;
end loop;
exception
when no_data_found then
null;
end;
exception
when others then
null; --showHTMLdoc(p_id);
end;
end showDoc;
end;
/
show errors
set define on
<%@ plsql procedure="search_html" %>
<%@ plsql parameter="query" default="null" %>
<%! v_results numeric := 0; %>
<html>
<head>
<title>search_html Search </title>
</head>
<body>
<%
If query is null Then
%>
<center>
<form method=post action="search_html">
<b>Search for: </b>
<input type=text name="query" size=30>
<input type=submit value=Search>
</center>
<hr>
<%
Else
%>
<p>
<%!
color varchar2(6) := 'ffffff';
%>
<center>
<form method=post action="search_html">
<b>Search for:</b>
<input type=text name="query" size=30 value="<%= query %>">
<input type=submit value=Search>
</form>
</center>
<hr>
<p>
<%
-- select statement
for doc in (
select /*+ DOMAIN_INDEX_SORT */ rowid, tk, title, score(1) scr
from search_table
where contains(text, query,1) >0
order by score(1) desc
)
loop
v_results := v_results + 1;
if v_results = 1 then
%>
<center>
<table border="0">
<tr bgcolor="#6699CC">
<th>Score</th>
<th>Title</th>
</tr>
<% end if; %>
<tr bgcolor="#<%= color %>">
<td> <%= doc.scr %>% </td>
<td> <%= doc.title %>
[<a href="search_htmlServices.showHTMLDoc?p_id=
<%= doc.tk %>">HTML</a>]
[<a href="search_htmlServices.showDoc?p_id=
<%= doc.tk %>&p_query=<%= query %>">Highlight</a>]
</td>
</tr>
<%
if (color = 'ffffff') then
color := 'eeeeee';
else
color := 'ffffff';
end if;
end loop;
%>
</table>
</center>
<%
end if;
%>
</body></html>
JSPベースのWebアプリケーションの作成は、PSPベースのアプリケーションの作成とほぼ同じ手順で行われます(「Webアプリケーションの作成」を参照してください)。同じloader.datファイルおよびloader.ctlファイルを使用できます。ただし、JSPベースのアプリケーションでは、次のことは不要です。
search_htmlservicesパッケージのコンパイル
search_htmlのPSPページのコンパイル(loadpspを使用)
このアプリケーションには、次の要件があります。
Oracle Database(リリース8.1.6以上)が起動され、実行中であること。
ApacheなどのWebサーバーが起動され、実行中であり、Oracle Databaseに要求を送信するように適切に構成されていること。
この項では、Webアプリケーションのサンプルの作成に使用するJavaコードを示します。次のファイルを記載しています。
このファイルのコードは、テキスト問合せアプリケーション・ウィザードで生成されました。(長い行の一部は、コードが読み取りやすいように分割されました。)
<%@ page import="java.sql.*, java.util.*, java.net.*,
oracle.jdbc.*, oracle.jsp.dbutil.*" %>
<%@ page contentType="text/html;charset=UTF-8" %>
<% oracle.jsp.util.PublicUtil.setReqCharacterEncoding(request, "UTF-8"); %>
<jsp:useBean id="name" class="oracle.jsp.jml.JmlString" scope ="request" >
<jsp:setProperty name="name" property="value" param="query" />
</jsp:useBean>
<%
String connStr="jdbc:oracle:thin:@jsmith-pc.us.oracle.com:1521:zippy922";
java.util.Properties info=new java.util.Properties();
Connection conn = null;
ResultSet rset = null;
OracleCallableStatement callStmt = null;
Statement stmt = null;
String userQuery = null;
String myQuery = null;
URLEncoder myEncoder;
int count=0;
int loopNum=0;
int startNum=0;
if (name.isEmpty()) {
%>
<html>
<title>Text Search</title>
<body>
<table width="100%">
<tr bgcolor="#336699">
<td><font face="arial, helvetica" align="left"
color="#CCCC99" size=+2>Text Search</td>
</tr>
</table>
<center>
<form method = post>
Search for:
<input type=text name=query size = 30>
<input type=submit value="Search">
</form>
</center>
</body>
</html>
<%}
else {
%>
<html>
<title>Text Search</title>
<body text="#000000" bgcolor="#FFFFFF" link="#663300"
vlink="#996633" alink="#ff6600">
<table width="100%">
<tr bgcolor="#336699">
<td><font face="arial, helvetica" align="left"
color="#CCCC99" size=+2>Text Search</td>
</tr>
</table>
<center>
<form method = post action="TextSearchApp.jsp">
Search for:
<input type=text name="query" value="<%=name.getValue() %>" size = 30>
<input type=submit value="Search">
</form>
</center>
<%
try {
DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver() );
info.put ("user", "jsmith");
info.put ("password","hello");
conn = DriverManager.getConnection(connStr,info);
stmt = conn.createStatement();
userQuery = request.getParameter("query");
myQuery = URLEncoder.encode(userQuery);
String numStr = request.getParameter("sn");
if(numStr!=null)
startNum=Integer.parseInt(numStr);
String theQuery = translate(userQuery);
callStmt =(OracleCallableStatement)conn.prepareCall("begin "+
"?:=ctx_query.count_hits(index_name=>'ULTRA_IDX1', "+
"text_query=>?"+
"); " +
"end; ");
callStmt.setString(2,theQuery);
callStmt.registerOutParameter(1, OracleTypes.NUMBER);
callStmt.execute();
count=((OracleCallableStatement)callStmt).getNUMBER(1).intValue();
if(count>=(startNum+20)){
%>
<font color="#336699" FACE="Arial,Helvetica" SIZE=+1>Results
<%=startNum+1%> - <%=startNum+20%> of <%=count%> matches
<%
}
else if(count>0){
%>
<font color="#336699" FACE="Arial,Helvetica" SIZE=+1>Results
<%=startNum+1%> - <%=count%> of <%=count%> matches
<%
}
else {
%>
<font color="#336699" FACE="Arial,Helvetica" SIZE=+1>No match found
<%
}
%>
<table width="100%">
<TR ALIGN="RIGHT">
<%
if((startNum>0)&(count<=startNum+20))
{
%>
<TD ALIGN="RIGHT">
<a href="TextSearchApp.jsp?sn=<%=startNum-20 %>&query=
<%=myQuery %>">previous20</a>
</TD>
<%
}
else if((count>startNum+20)&(startNum==0))
{
%>
<TD ALIGN="RIGHT">
<a href="TextSearchApp.jsp?sn=<%=startNum+20
%>&query=<%=myQuery %>">next20</a>
</TD>
<%
}
else if((count>startNum+20)&(startNum>0))
{
%>
<TD ALIGN="RIGHT">
<a href="TextSearchApp.jsp?sn=<%=startNum-20 %>&query=
<%=myQuery %>">previous20</a>
<a href="TextSearchApp.jsp?sn=<%=startNum+20 %>&query=
<%=myQuery %>">next20</a>
</TD>
<%
}
%>
</TR>
</table>
<%
String ctxQuery = "select /*+ DOMAIN_INDEX_SORT */ rowid, 'TITLE',
score(1) scr from 'ULTRA_TAB1' where contains('TEXT', '"+theQuery+"',1 )
> 0 order by score(1) desc";
rset = stmt.executeQuery(ctxQuery);
String color = "ffffff";
String rowid = null;
String fakeRowid = null;
String[] colToDisplay = new String[1];
int myScore = 0;
int items = 0;
while (rset.next()&&items< 20) {
if(loopNum>=startNum)
{
rowid = rset.getString(1);
fakeRowid = URLEncoder.encode(rowid);
colToDisplay[0] = rset.getString(2);
myScore = (int)rset.getInt(3);
items++;
if (items == 1) {
%>
<center>
<table BORDER=1 CELLSPACING=0 CELLPADDING=0 width="100%"
<tr bgcolor="#CCCC99">
<th><font face="arial, helvetica" color="#336699">Score</th>
<th><font face="arial, helvetica" color="#336699">TITLE</th>
<th> <font face="arial, helvetica"
color="#336699">Document Services</th>
</tr>
<% } %>
<tr bgcolor="#FFFFE0">
<td ALIGN="CENTER"> <%= myScore %>%</td>
<td> <%= colToDisplay[0] %>
<td>
</td>
</tr>
<%
if (color.compareTo("ffffff") == 0)
color = "eeeeee";
else
color = "ffffff";
}
loopNum++;
}
} catch (SQLException e) {
%>
<b>Error: </b> <%= e %><p>
<%
} finally {
if (conn != null) conn.close();
if (stmt != null) stmt.close();
if (rset != null) rset.close();
}
%>
</table>
</center>
<table width="100%">
<TR ALIGN="RIGHT">
<%
if((startNum>0)&(count<=startNum+20))
{
%>
<TD ALIGN="RIGHT">
<a href="TextSearchApp.jsp?sn=<%=startNum-20 %>&query=
<%=myQuery %>">previous20</a>
</TD>
<%
}
else if((count>startNum+20)&(startNum==0))
{
%>
<TD ALIGN="RIGHT">
<a href="TextSearchApp.jsp?sn=<%=startNum+20 %>&query=
<%=myQuery %>">next20</a>
</TD>
<%
}
else if((count>startNum+20)&(startNum>0))
{
%>
<TD ALIGN="RIGHT">
<a href="TextSearchApp.jsp?sn=<%=startNum-20 %>&query=
<%=myQuery %>">previous20</a>
<a href="TextSearchApp.jsp?sn=<%=startNum+20 %>&query=
<%=myQuery %>">next20</a>
</TD>
<%
}
%>
</TR>
</table>
</body></html>
<%}
%>
<%!
public String translate (String input)
{
Vector reqWords = new Vector();
StringTokenizer st = new StringTokenizer(input, " '", true);
while (st.hasMoreTokens())
{
String token = st.nextToken();
if (token.equals("'"))
{
String phrase = getQuotedPhrase(st);
if (phrase != null)
{
reqWords.addElement(phrase);
}
}
else if (!token.equals(" "))
{
reqWords.addElement(token);
}
}
return getQueryString(reqWords);
}
private String getQuotedPhrase(StringTokenizer st)
{
StringBuffer phrase = new StringBuffer();
String token = null;
while (st.hasMoreTokens() && (!(token = st.nextToken()).equals("'")))
{
phrase.append(token);
}
return phrase.toString();
}
private String getQueryString(Vector reqWords)
{
StringBuffer query = new StringBuffer("");
int length = (reqWords == null) ? 0 : reqWords.size();
for (int ii=0; ii < length; ii++)
{
if (ii != 0)
{
query.append(" & ");
}
query.append("{");
query.append(reqWords.elementAt(ii));
query.append("}");
}
return query.toString();
}
%>