Oracle9i中全文檢索的創建與使用

Oracle9i中全文檢索的創建與使用 
1前言
 
Oracle7.3開始支持全文檢索,即用戶可以使用Oracle服務器的上下文(ConText)選項完成基於文本的查詢。具體可以採用通配符查找、模糊匹配、相關分類、近似查找、條件加權和詞意擴充等方法。在Oracle8.0.x中稱爲ConText ;在Oracle8i中稱爲interMedia Text  Oracle9i中稱爲Oracle Text

本篇主要介紹Oracle Text的基本結構和簡單應用。

Oracle Text是9i標準版和企業版的一部分。Oracle9i將全文檢索功能做爲內置功能提供給用戶,使得用戶在創建數據庫實例時自動安裝全文檢索。
Oracle Text的應用領域有很多:

l         搜索文本:需要快捷有效搜索文本數據的應用程序
l         管理多種文檔:允許搜索各種混和文檔格式的應用程序,包括ord,excel,lotus
l         從多種數據源中檢索文本:不僅來自Oracle數據庫中的文本數據,而且可以來自Internet和文件系統的文本數據
l         搜索XML應用程序
 
1.1搜索文本

不使用Oracle text功能,也有很多方法可以在Oracle數據庫中搜索文本.可以使用標準的INSTR函數和LIKE操作符實現.

SELECT *
  FROM mytext
 WHERE INSTR (thetext, 'Oracle') > 0;

SELECT *
  FROM mytext
 WHERE thetext LIKE '%Oracle%';

有很多時候使用instrlike是很理想的, 特別是搜索僅跨越很小的表的時候.然而通過這些文本定位的方法將導致全表掃描,對資源來說消耗比較昂貴,而且實現的搜索功能也非常有限.
 
利用Oracle Text,你可以回答如“在存在單詞‘Oracle’的行同時存在單詞’Corporation’而且兩單詞間距不超過10個單詞的文本‘,’查詢含有單詞’Oracle’或者單詞’ california’的文本,並且將結果按準確度進行排序‘,’含有詞根train的文本‘。以下的sql代碼實現瞭如上功能。我們且不管這些語法是如何使用的。

DROP INDEX index mytext_idx
/
 
CREATE INDEX mytext_idx
ON mytext( thetext )
INDEXTYPE is CTXSYS.CONTEXT
/
 
SELECT id
  FROM mytext
 WHERE contains (thetext, 'near((Oracle,Corporation),10)') > 0
 
SELECT score (1), id
    FROM mytext
   WHERE contains (thetext, 'Oracle or california', 1) > 0
ORDER BY score (1) DESC
/
 
SELECT id
  FROM mytext
 WHERE contains (thetext, '$train') > 0;
 
 
1.2設置
 
首先檢查數據庫中是否有CTXSYS用戶和CTXAPP腳色。如果沒有這個用戶和角色,意味着你的數據庫創建時未安裝intermedia功能。你必須修改數據庫以安裝這項功能。
還可以檢查服務器是否有對PLSExtProc服務的監聽。

lsnrctl status  
should give status
          LSNRCTL for Solaris: Version
          8.1.5.0.0 - Production on 31-MAR-99    18:57:49
 
          (c) Copyright 1998 Oracle Corporation. All rights reserved.
 
          Connecting to
          (DESCRIPTION=(ADDRESS=(PROTOCOL=IPC)(KEY=EXTPROC0)))
          STATUS of the LISTENER
          ------------------------
 
          Alias LISTENER
          Version TNSLSNR for Solaris: Version 8.1.5.0.0 - Production
          Start Date 30-MAR-99 15:53:06
          Uptime 1 days 3 hr. 4 min. 42 sec
          Trace Level off
          Security OFF
          SNMP OFF
          Listener Parameter File
          /private7/Oracle/Oracle_home/network/admin/listener.ora
          Listener Log File
          /private7/Oracle/Oracle_home/network/log/listener.log
          Services Summary...
          PLSExtProc has 1 service handler(s)
          oco815 has 3 service handler(s)
          The command completed successfully  
 
Oracle 是通過所謂的‘外部調用功能’(external procedure)來實現intermedia的。
 
B. Create a user/table/index/query thus: As SYS or SYSTEM:

----------------------------------------------------------
CREATE USER ctxtest IDENTIFIED BY ctxtest;
GRANT CONNECT, RESOURCE, ctxapp TO ctxtest;      
----------------------------------------------------------
Do any other grants, quotas, tablespace etc. for the new user. As CTXTEST:
----------------------------------------------------------
CREATE TABLE quick  (
             quick_id NUMBER PRIMARY KEY,              
             text VARCHAR(80));

INSERT INTO  quick
             (quick_id, text)
     VALUES  (1, 'The cat sat on the mat');

INSERT INTO  quick
             (quick_id, text)
     VALUES  (2, 'The quick brown fox jumped over the lazy dog');
COMMIT ;       
    
CREATE INDEX quick_text
          ON quick ( text )
INDEXTYPE IS ctxsys.CONTEXT;  
----------------------------------------------------------
 
             
此時如果監聽沒有配置好創建索引將會失敗。
 
CREATE INDEX quick_text ON quick ( text )

* 
           ERROR at line 1: 
           ORA-29855: error occurred in the execution of ODCIINDEXCREATE routine 
           ORA-20000: ConText error: 
           DRG-50704: Net8 listener is not running or cannot start external procedures 
           ORA-28575: unable to open RPC connection to external procedure agent 
           ORA-06512: at "CTXSYS.DRUE", line 122 
           ORA-06512: at "CTXSYS.TEXTINDEXMETHODS", line 34 
           ORA-06512: at line 1 
      
如果一切正常,你將得到:
           Index created. 
    
現在嘗試寫一些查詢:
           SQL> SELECT quick_id FROM quick  WHERE contains (text, 'cat') > 0;  
     
   QUICK_ID 
           ---------- 
           1 
                
           SQL> SELECT quick_id FROM quick WHERE contains(text, 'fox') > 0; 
 
           QUICK_ID 
           ---------- 
           2
 
 
2建立索引
 
21索引簡介
 
利用Oracle Text對文檔集合進行檢索的時候,你必須先在你的文本列上建立索引。索引將文本打碎分成很多記號(token),這些記號通常是用空格分開的一個個單詞。
 
Oracle Text應用的實現實際上就是一個數據裝載—> 索引數據—>執行檢索的一個過程。

 
211索引類型和限制
 
建立的Oracle Text索引被稱爲域索引(domain index),包括4種索引類型: 

l          CONTEXT
l          CTXCAT
l          CTXRULE 
l          CTXXPATH
 
依據你的應用程序和文本數據類型你可以任意選擇一種。可以利用Create Index建立這4種索引。下面說一下這4種索引的使用環境。

索引類型
描述
查詢操作符
CONTEXT
用於對含有大量連續文本數據進行檢索。支持word、html、xml、text等很多數據格式。支持中文字符集,支持分區索引,唯一支持並行創建索引(Parallel indexing)的索引類型。
對錶進行DML操作後,並不會自動同步索引。需要手工同步索引
CONTAINS
CTXCAT
當使用混合查詢語句的時候可以帶來很好的效率。適合於查詢較小的具有一定結構的文本段。具有事務性,當更新主表的時候自動同步索引。
The CTXCAT index does not support table and index partitioning, documents services (highlighting, markup, themes, and gists) or query services (explain, query feedback, and browse words.)
CATSEARCH
CTXRULE
Use to build a document classification application. You create this index on a table of queries, where each query has a classification.
Single documents (plain text, HTML, or XML) can be classified by using the MATCHES operator.
MATCHES
CTXXPATH
Create this index when you need to speed up ExistsNode() queries on an XMLType column.
Can only create this index on XMLType column.
 
 
在以上4種索引中,最常用的就是 CONTEXT索引,使用最通用的CONTAINS操作符進行查詢。本篇主要針對的就是Oracle Text ConText的介紹

212權限和臨時表空間
權限
用戶不需要具有CTXAPP腳色便可以建立Oracle Text索引。你只需要對某一文本列有創建B樹索引的權限。查詢用戶,索引用戶,表擁有者可以是不同的用戶。
臨時表空間要求
建立Oracle Text索引需要消耗CTXSYS用戶默認的臨時表空間空間。如果空間不夠的話將導致 ORA-01652 錯誤。你可以擴展CTXSYS的臨時表空間,而不是發出命令的用戶默認的臨時表空間。
對於索引全英文的文本列來說,需要臨時表空間大小通常是其文本數據量的50%-200%不等。而對索引包含中文文本列來說需要的表空間會更多。

2.2 CONTEXT 索引
 
2.2.1 CONTEXT 索引的結構
Oracle Text 索引將文本打碎分成很多的記號(token.例如文本‘I Love www.itpub.net’將會被分成 I ,LOVE,WWW,ITPUB,NET這樣的記號(token)。
Oracle Text CONTEXT 索引是反向索引(inverted index)。每個記號token)都映射着包含它自己的文本位置。在索引建立過程中,單詞Cat會包括如下的條目入口:
Cat row1,row2,row3
表示Cat在行row1row2row3都出現過,這樣通過查找單詞所對應的行的rowid就可以迅速找到文本記錄。
在索引建好後,我們可以在該用戶下查到Oracle自動產生了以下幾個表:(假設索引名爲myindex):DR$myindex$IDR$myindex$KDR$myindex$RDR$myindex$N其中以I表最重要,可以查詢一下該表,看看有什麼內容:

SQL> CREATE TABLE mytext (text VARCHAR2(100));
SQL> INSERT INTO mytext
     VALUES ('I Love www.itpub.net');
SQL> COMMIT;
SQL> CREATE INDEX mytext_idx ON mytext(text) INDEXTYPE IS ctxsys.CONTEXT;
SQL> SELECT token_text, token_count FROM dr$mytext_idx$i;

TOKEN_TEXT                           TOKEN_COUNT
I                                             1                     
ITPUB                                         1                               
LOVE                                          1                        
NET                                           1                             
WWW                                           1
注意TOKEN_TEXT裏面字符全部是大寫的,默認情況下全文索引是不區分大小寫的.                                                                  
SQL> DESC dr$mytext_idx$i;
名稱                                      是否爲空 類型
TOKEN_TEXT                                NOT NULL VARCHAR2(64)
TOKEN_TYPE                                NOT NULL NUMBER(3)
TOKEN_FIRST                               NOT NULL NUMBER(10)
TOKEN_LAST                                NOT NULL NUMBER(10)
TOKEN_COUNT                               NOT NULL NUMBER(10)
TOKEN_INFO                                         BLOB
 
可以看到,該表中保存的其實就是Oracle 分析你的文檔後,生成的token記錄在這裏,包括token出現的位置、次數、hash值等。
2.2.2建立索引
建立索引的語法如下。索引建立好後可以用ConTains進行查詢。
 
CREATE INDEX [schema.]index on [schema.]table(column) INDEXTYPE IS ctxsys.context [ONLINE]
LOCAL [(PARTITION [partition] [PARAMETERS('paramstring')]
[, PARTITION [partition] [PARAMETERS('paramstring')]])]
[PARAMETERS(paramstring)] [PARALLEL n] [UNUSABLE];
 
數據庫用創建和插入這些索引的方法叫做索引管道(index Pipeline)。根據不同的參數構建索引,可以應用於很多實際環境。
類別
描述
Datastore
從哪裏得到數據?
Filter
將數據轉換成文本
Lexer
正在索引什麼語言?
Wordlist
應該如何展開莖幹和模糊查詢
Storage
如何存儲索引 
Stop List
什麼單詞或者主題不被索引?
Section Group
允許在區段內查詢嗎?如何定義文檔區段。這把文檔轉換成普通文本
這些參數在建立CONTEXT索引過程中將按下圖順序對索引進程起作用。在本篇中提供一些簡單demo會看到各個參數的作用。
Default CONTEXT Index Example
建立索引時,系統默認文檔存儲在數據庫的文本列中。如果不顯示的指定索引參數,系統會自動探測文本語言,數據類型和文檔格式。

CREATE INDEX myindex ON docs(text) INDEXTYPE IS CTXSYS.CONTEXT;
如上命令建立了一個默認參數的CONTEXT索引myindex.系統默認:
1.        文本存儲在數據庫中。可以是CLOB, BLOB, BFILE, VARCHAR2, or CHAR類型的文本數據。
2.        文本列語言是數據庫建立時的默認的字符集。
3.        使用數據庫默認的終止目錄stoplist.stoplist記錄存在於文本列中但不對其索引的詞。
4.        允許模糊查詢。
 
2.3索引參數
 
2.3.1DataStore

指明你的文本是如何存儲的。系統默認文檔儲存在數據庫內的文本列(CHAR, VARCHAR, VARCHAR2, BLOB, CLOB, BFILE, or XMLType)中。DataStore對象在由過濾器處理之前從數據庫中的列摘錄文本。你要索引的文檔可以來自多種數據源。


Datastore Type
Use When
Data is stored internally in the text column. Each row is indexed as a single document.
Data is stored in a text table in more than one column. Columns are concatenated to create a virtual document, one per row.
Data is stored internally in the text column. Document consists of one or more rows stored in a text column in a detail table, with header information stored in a master table.
Data is stored externally in operating system files. Filenames are stored in the text column, one per row.
Data is stored in a nested table.
Data is stored externally in files located on an intranet or the Internet. Uniform Resource Locators (URLs) are stored in the text column.
Documents are synthesized at index time by a user-defined stored procedure.
DataStore參數應用.

:我有一張表記錄着僱員的名稱和住址,我想在這兩列中查找某個單詞是否存在這兩個列之一。
:方法1,在僱員名稱和住址上建立兩個ConText索引。查詢:
SELECT *
  FROM emp
 WHERE contains (ename, 'biti') > 0
    OR contains (address, 'biti') > 0;
方法2, 定製
n      Only CTXSYS is allowed to create preferences for the MULTI_COLUMN_DATASTORE type. Any other user who attempts to create a MULTI_COLUMN_DATASTORE preference receives an error.so it run on ctxsys schema
 
CREATE TABLE mc(id NUMBER PRIMARY KEY, NAME VARCHAR2(10), address VARCHAR2(80))
/
 
INSERT INTO mc
     VALUES (1, 'John Smith', '123 Main Street biti');
EXEC ctx_ddl.create_preference('mymds', 'MULTI_COLUMN_DATASTORE');
EXEC ctx_ddl.set_attibute('mymds', 'columns', 'name, address');
CREATE INDEX mc_idx ON mc(NAME) INDEXTYPE IS ctxsys.CONTEXT PARAMETERS('datastore mymds')
/
 
SELECT *
  FROM mc
 WHERE contains (name, 'biti') > 0;
 
 
:如何實現對主/從表的全文檢索?
 
:使用類Detail_DataStore。這個類經過設計,供主/從表格使用,其中大量文本存儲在從表表格列中。在進行索引之前把多個從錶行聯接爲一個文檔,使用外來關鍵碼識別行,外鍵關係必須已邏輯的方式存在,但不必作爲數據庫約束條件。注意在不更改主表的情況下更改從表,不會更新索引。解決這個的辦法是更新主列的值,觸發索引的重新構建,或者手工設置和重新構建索引。否則,應該存在未使用的列,來保持SQL語法的完整性。如下面例子中的purchase_order表的line_item_body列。

------------------BEGIN---------------------
SET echo on
DROP TABLE purchase_order;
CREATE TABLE purchase_order
( id                  NUMBER PRIMARY KEY,
  description         VARCHAR2(100),
  line_item_body      CHAR(1)
)
/
DROP TABLE line_item;
CREATE TABLE line_item
( po_id            NUMBER,
  po_sequence      NUMBER,
  line_item_detail VARCHAR2(1000)
)
/
INSERT INTO purchase_order
            (id, description)
     VALUES (1, 'Many Office Items')
/
INSERT INTO line_item
            (po_id, po_sequence, line_item_detail)
     VALUES (1, 1, 'Paperclips to be used for many reports')
/
INSERT INTO line_item
            (po_id, po_sequence, line_item_detail)
     VALUES (1, 2, 'Some more Oracle letterhead')
/
INSERT INTO line_item
            (po_id, po_sequence, line_item_detail)
     VALUES (1, 3, 'Optical mouse')
/
COMMIT ;
BEGIN
   ctx_ddl.create_preference ('po_pref', 'DETAIL_DATASTORE');
   ctx_ddl.set_attribute ('po_pref', 'detail_table', 'line_item');
   ctx_ddl.set_attribute ('po_pref', 'detail_key', 'po_id');
   ctx_ddl.set_attribute ('po_pref', 'detail_lineno', 'po_sequence');
   ctx_ddl.set_attribute ('po_pref', 'detail_text', 'line_item_detail');
END;
/
DROP INDEX po_index;
CREATE INDEX po_index ON purchase_order( line_item_body )
INDEXTYPE IS ctxsys.CONTEXT
PARAMETERS( 'datastore po_pref' )
/
SELECT id
  FROM purchase_order
 WHERE contains (line_item_body, 'Oracle') > 0
/
-------------------END----------------------


2.3.2 Filter 過濾
 
一旦彙編了文檔,它就沿管道傳遞。接下來這個階段是過濾(Filter).如果文檔是一種外來格式,就將它轉換爲可讀取的文本,以便進行索引。默認是NULL_FILTER,它簡單的直接傳遞文檔,不作任何修改。
通常我們使用NULL_FILTER 過濾普通文本和HTML文檔。下面是一個索引HTML文檔的例子。

CREATE INDEX myindex
             ON docs(htmlfile)
INDEXTYPE IS ctxsys.CONTEXT   PARAMETERS(
'filter ctxsys.null_filter  section group ctxsys.html_section_group');

我們使用null_filter過濾類和ctxsys用戶自帶的 html_section_group區段組類。我們會在後面馬上介紹區段組(Section Groups)的概念。
 
2.3.2 Section Groups區分組

區分組(Section Groups)是與interMedia一起使用XML的關鍵。這些組處理XML(或者HTML)文檔,輸出兩個數據流,即區段界限和文本內容。默認是NULL_SECTION_GROUP,它簡單的直接傳遞文本,不執行任何修改和處理。HTML_SECTION_GROUP是專門用來處理HTML文檔的。

下面的例子中顯示如何處理HTML文檔

------------------BEGIN---------------------

SET echo on
DROP TABLE my_html_docs;
CREATE TABLE my_html_docs( id NUMBER PRIMARY KEY,  html_text VARCHAR2(4000))
/

INSERT INTO my_html_docs
            (id,
             html_text)
     VALUES (1,
             '<html><title>Oracle Technology</title><body>This is about the wonderful marvels of 8i and 9i</body></html>')
/
 
COMMIT ;
 
CREATE INDEX my_html_idx ON my_html_docs( html_text )INDEXTYPE IS ctxsys.CONTEXT
/

-- 默認使用NULL_SECTION_GROUP 不對文檔做任何數據流處理

SELECT id
  FROM my_html_docs
 WHERE contains (html_text, 'Oracle') > 0
/
-- 可以檢索到區段界限之間的文本

SELECT id
  FROM my_html_docs
 WHERE contains (html_text, 'title') > 0
/

SELECT id
  FROM my_html_docs
 WHERE contains (html_text, 'html') > 0
/            
/* 也可以檢索到 區段界限內的文本。但由於HTML文件中的一些標記如<b> <body> <A href=…..>等對我們沒有提供有用的信息。而且在索引中還浪費了空間和CPU。因此HTML標記不應該被索引 */
--我們可以定製我們自己的區段標記。可以查詢在某個區段出現的單詞

BEGIN
   ctx_ddl.create_section_group ('my_section_group', 'BASIC_SECTION_GROUP');
   ctx_ddl.add_field_section (
      group_name=> 'my_section_group',
      section_name=> 'Title',
      tag   => 'title',
      visible=> FALSE
   );
END;
/

DROP INDEX my_html_idx;
CREATE INDEX my_html_idx ON my_html_docs( html_text )
INDEXTYPE IS ctxsys.CONTEXT
PARAMETERS( 'section group my_section_group' )
/

SELECT id
  FROM my_html_docs
 WHERE contains (html_text, 'Oracle within title') > 0;

------------------END---------------------

 
下面是如何檢索XML文檔的例子
InterMedia Text 支持索引XML文檔通過指定區段組。區段組就是XML文檔中預先定義的節點.你可以用WithIn在指定檢索某個節點,提高了檢索的準確性。
 
1) 首先,創建一個表來存儲我們的XML文檔:

CREATE TABLE employee_xml(
             id NUMBER PRIMARY KEY,
             xmldoc CLOB )
/

2) 插入一個簡單的文檔(the DTD is not required)

INSERT INTO employee_xml
VALUES (1,
 '<?xml version="1.0"?>
<!DOCTYPE employee [
<!ELEMENT employee (Name, Dept, Title)>
<!ELEMENT Name (#PCDATA)>
<!ELEMENT Dept (#PCDATA)>
<!ELEMENT Title (#PCDATA)>
]>
<employee>
<Name>Joel Kallman</Name>
<Dept>Oracle Service Industries Technology Group</Dept>
<Title>Technologist</Title>
</employee>');
 
3)創建一個叫'xmlgroup'的interMedia Text section group , 添加 Name和Dept tag到section group中。(Caution: in XML, tag names are case-sensitive, but
  tag names in section groups are case-insensitive)

BEGIN
   ctx_ddl.create_section_group ('xmlgroup', 'XML_SECTION_GROUP');
   ctx_ddl.add_zone_section ('xmlgroup', 'Name', 'Name');
   ctx_ddl.add_zone_section ('xmlgroup', 'Dept', 'Dept');
END;
 
4)Create our interMedia Text index, specifying the section  group we created above. 
Also, specify the null_filter, as the Inso filter is not required.
 
CREATE INDEX employee_xml_index
          ON employee_xml( xmldoc )
INDEXTYPE IS ctxsys.CONTEXT    PARAMETERS(
'filter ctxsys.null_filter section group xmlgroup' )
/
 
5) 現在,執行一個查詢,搜尋特定Section中的Name:

   SELECT id
     FROM employee_xml
    WHERE contains (xmldoc, 'Joel within Name') > 0;

6)Only non-empty tags will be indexed, but not the tag names themselves.
 Thus, the following queries will return zero rows.
 
  SELECT id
    FROM employee_xml
   WHERE contains (xmldoc, 'title') > 0;
  SELECT id
    FROM employee_xml
   WHERE contains (xmldoc, 'employee') > 0;
 
7) But the following query will locate our document, even though we have not defined
 Title as a section.
 
    SELECT id
      FROM employee_xml
     WHERE contains (xmldoc, 'Technologist') > 0;
 
Let's say you want to get going right away with indexing XML, and don't want to have to specify sections for every element in your XML document collection.  You can do this very easily by using the predefined AUTO_SECTION_GROUP.  This section group is exactly like the XML section group, but the pre-definition of sections is not required.  For all non-empty tags in your document, a zone section will be created with the  section name the same as the tag name.
Use of the AUTO_SECTION_GROUP is also ideal when you may not know in advance all of the tag names that will be a part of your XML document set.

8) Drop our existing interMedia Text index.

9)And this time, recreate it specifying the AUTO_SECTION_GROUP.
  We do not need to predefine the sections of our group, it is handled for us Automatically.

DROP INDEX employee_xml_index
/
CREATE INDEX employee_xml_index ON employee_xml( xmldoc )
INDEXTYPE IS ctxsys.CONTEXT PARAMETERS( 'filter ctxsys.null_filter section group ctxsys.auto_section_group' )
/

10) 再一次,我們使用Section查找定位我們的文檔:
 
SELECT id
  FROM employee_xml
 WHERE contains (xmldoc, 'Technologist within Title') > 0;
 
 
 
233 Storage 
Storage(存儲空間)組的類只含有BASIC_STORAGE.默認情況下,BASIC_STORAGE對象的屬性是空的。我們通常需要定製自己的STORAGE類,來控制索引的存儲參數以及存儲空間。建立全文索引的時候我們通常會考慮表段dr$indexname$I,,dr$indexname$R,索引段dr$indexname$X的空間分配。
類型
描述
BASIC_STORAGE
爲CONTEXT索引指定默認的存儲參數
BASIC_STORAGE 有如下參數
屬性
屬性值
i_table_clause
Parameter clause for dr$indexname$I table creation. Specify storage and tablespace clauses to add to the end of the internal CREATE TABLE statement.
The I table is the index data table.
k_table_clause
Parameter clause for dr$indexname$K table creation. Specify storage and tablespace clauses to add to the end of the internal CREATE TABLE statement.
The K table is the keymap table.
r_table_clause
Parameter clause for dr$indexname$R table creation. Specify storage and tablespace clauses to add to the end of the internal CREATE TABLE statement.
The R table is the rowid table.
The default clause is: 'LOB(DATA) STORE AS (CACHE)'
n_table_clause
Parameter clause for dr$indexname$N table creation. Specify storage and tablespace clauses to add to the end of the internal CREATE TABLE statement.
The N table is the negative list table.
i_index_clause
Parameter clause for dr$indexname$X index creation. Specify storage and tablespace clauses to add to the end of the internal CREATE INDEX statement. The default clause is: 'COMPRESS 2' which instructs Oracle to compress this index table.
If you choose to override the default, Oracle recommends including COMPRESS 2 in your parameter clause to compress this table, since such compression saves disk space and helps query performance.
p_table_clause
Parameter clause for the substring index if you have enabled SUBSTRING_INDEX in the BASIC_WORDLIST.
Specify storage and tablespace clauses to add to the end of the internal CREATE INDEX statement. The P table is an index-organized table so the storage clause you specify must be appropriate to this type of table.
默認情況下,4個表段和1個索引段將會建立在擁有該表的用戶的默認表空間下。如下:

CREATE INDEX iowner.idx ON towner.tab(b) INDEXTYPE IS ctxsys.CONTEXT;
索引將會建立在IOWNER用戶的默認表空間下,而不管發出該語句的用戶是否是IOWNER。
Storage 範例
下面我們自己定製CONTEXT索引的存儲選項。可以爲各個段指定不同的表空間。

EXECUTE ctx_ddl.drop_preference('mystore');
BEGIN
   ctx_ddl.create_preference ('mystore', 'BASIC_STORAGE');
   ctx_ddl.set_attribute ('mystore', 'I_TABLE_CLAUSE', 'tablespace indx ');
   ctx_ddl.set_attribute (
      'mystore',
      'I_INDEX_CLAUSE',
      'tablespace users03 compress 2 '
   );
END;

--ORACLE推薦DR$INDX$X進行壓縮 COMPRESS 2
 
CREATE INDEX html1_idx
          ON html1(newsdescription)
INDEXTYPE IS ctxsys.CONTEXT PARAMETERS('storage mystore');
 
---查看段的存儲參數,這樣方便了以後數據容量的擴展。
SELECT segment_name, tablespace_name
  FROM user_segments
 WHERE segment_name LIKE '%HTML1%';


2.3.4設置詞法分析器(lexer)

Oracle實現全文檢索,其機制其實很簡單。即通過Oracle專利的詞法分析器(lexer),將文章中所有的表意單元(Oracle 稱爲 term)找出來,記錄在一組 以dr$開頭的表中,同時記下該term出現的位置、次數、hash 值等信息。檢索時,Oracle 從這組表中查找相應的term,並計算其出現頻率,根據某個算法來計算每個文檔的得分(score),即所謂的‘匹配率’。而lexer則是該機制的核心,它決定了全文檢索的效率。Oracle 針對不同的語言提供了不同的 lexer, 而我們通常能用到其中的三個:
 
n      basic_lexer: 針對英語。它能根據空格和標點來將英語單詞從句子中分離,還能自動將一些出現頻率過高已經失去檢索意義的單詞作爲‘垃圾’處理,如if , is 等,具有較高的處理效率。但該lexer應用於漢語則有很多問題,由於它只認空格和標點,而漢語的一句話中通常不會有空格,因此,它會把整句話作爲一個term,事實上失去檢索能力。以‘中國人民站起來了’這句話爲例,basic_lexer 分析的結果只有一個term ,就是‘中國人民站起來了’。此時若檢索‘中國’,將檢索不到內容。
n      chinese_vgram_lexer: 專門的漢語分析器,支持所有漢字字符集(ZHS16CGB231280 ZHS16GBK ZHT32EUC ZHT16BIG5 ZHT32TRIS ZHT16MSWIN950 ZHT16HKSCS UTF8 )。該分析器按字爲單元來分析漢語句子。‘中國人民站起來了’這句話,會被它分析成如下幾個term: ‘中’,‘中國’,‘國人’,‘人民’,‘民站’,‘站起’,起來’,‘來了’,‘了’。可以看出,這種分析方法,實現算法很簡單,並且能實現‘一網打盡’,但效率則是差強人意。
n      chinese_lexer: 這是一個新的漢語分析器,只支持utf8字符集。上面已經看到,chinese vgram lexer這個分析器由於不認識常用的漢語詞彙,因此分析的單元非常機械,像上面的‘民站’,‘站起’在漢語中根本不會單獨出現,因此這種term是沒有意義的,反而影響效率。chinese_lexer的最大改進就是該分析器 能認識大部分常用漢語詞彙,因此能更有效率地分析句子,像以上兩個愚蠢的單元將不會再出現,極大 提高了效率。但是它只支持 utf8, 如果你的數據庫是zhs16gbk字符集,則只能使用笨笨的那個Chinese vgram lexer.
如果不做任何設置,Oracle 缺省使用basic_lexer這個分析器。要指定使用哪一個lexer, 可以這樣操作:
 
第一.   在ctxsys用戶下建立一個preference:

BEGIN
  ctx_ddl.create_preference ('my_lexer', 'chinese_vgram_lexer');
END;
 
第二.   在建立intermedia索引時,指明所用的lexer:

CREATE INDEX  myindex ON mytable(mycolumn) indextype is ctxsys.context 
parameters('lexer my_lexer');

這樣建立的全文檢索索引,就會使用chinese_vgram_lexer作爲分析器。 相應的,索引中文就比索引英文佔用的表空間多了許多。Oracle Text爲了性能不得不犧牲了空間。如下是我的簡單存儲空間測試:
 
文本數據量
索引數據量(4個表段和1個索引段)
6M
80M
80M
900M
230M
2880M
1344M
15232M
 

2.3.5 STOP Lists


Stop List只不過是被索引忽略的單詞的列表。這些通常是常見的單詞,正常情況下不會以任何方式查詢它們,因此,索引它們純粹是表格空間和處理器週期的浪費。在具體的應用中,可能存在這樣的單詞,它們在特定的文集中出現的頻率太大,無法提供有意義的內容,特別是常用的單詞。

Stop List可以含有最多4095個單詞,每個單詞最多64個字符,同時爲英語和其它語言提供了默認列表。

下圖是Chinese Stoplist (Simplified)的默認列表:

你可以查看英文的默認列表:

SELECT spw_word FROM DR$STOPWORD;
你可以查詢ctx_stoplistsctx_stopwords 視圖來觀察這些語言。
EXECUTE ctx_ddl.create_stoplist('stoppref');

SELECT *
  FROM ctx_stoplists;

SPL_OWNER  SPL_NAME       SPL_COUNT     SPL_TYPE
CTXSYS    STOPPREF          0          BASIC_STOPLIST

EXECUTE ctx_ddl.add_stopword('stoppref','的');

SELECT *
  FROM ctx_stoplists;

SPL_OWNER  SPL_NAME       SPL_COUNT     SPL_TYPE
CTXSYS    STOPPREF         1         BASIC_STOPLIST

SELECT spw_word
  FROM dr$stopword;
 
 
2.3.5 Lists
 
要考慮的最後一個類是單一的Word List類,即BASIC_WORDLIST。創建索引時不使用這個類,這個類只在某些高級形式的查詢中使用。莖幹查詢使用從Xerox公司許可的技術,來匹配單詞與通用的語言根。
 
2.3.6其它選項
 
MEMORY參數以通常的方式附着到CREATE INDEX中的PARAMETERS上,設置用於構建或更改索引的內存量。這個量不能超過MAX_INDEX_MEMEORY,
使用CTX_ADM.SET_PARAMETER 對其進行設置。

查看系統默認參數項:

SELECT par_name, par_value FROM ctx_parameters;

設置系統默認參數:

CTX_ADM.SET_PARAMETER(param_name IN VARCHAR2,
                      param_value IN VARCHAR2);
 
Oracle Text使用的索引機制比通常的ORACLE B-TREE索引更復雜,且文檔實際是在內存中構建的,而不是一次一行的添加到B-TREE。到達內存參數指定的值時,更新磁盤山的索引,接着,緩衝區由下一組文檔重用。任一時刻緩衝區內的文檔數會有所不同,並且在索引處理之前不進行任何排序。因此,在少量的內存中索引大量文檔會導致出現碎片索引情況。

3索引維護
 
這節講一下當索引操作失敗的時候所要進行的一些處理。
 
3.1查看索引錯誤
 
有時候,索引操作可能失敗或者沒有成功結束。當發生錯誤的時候,它會將錯誤記錄在系統表中。
 
你可以查看某個用戶下TEXT索引發生的錯誤CTX_USER_INDEX_ERRORS;也可以查詢CTXSYS. CTX_INDEX_ERRORS查看全部用戶的索引錯誤。.
 
查看最近發生的錯誤:

  
SELECT err_timestamp, err_text
    FROM ctx_user_index_errors
ORDER BY err_timestamp DESC;

清除錯誤視圖:

DELETE FROM ctx_user_index_errors;
 
 
3.2刪除索引
例如,刪除索引名稱爲newindex的索引。

DROP INDEX newsindex;
如果Oracle不能決定索引的狀態,例如建立索引的時候發生錯誤,沒有成功結束,則需要強制刪除索引。

DROP INDEX newsindex FORCE;
3.3索引重建
使用ALTER INDEX重建索引。你可能需要用新的參數重新建立索引。例如可能想用新的詞法分析器替換索引現有的:

ALTER INDEX newsindex REBUILD PARAMETERS('replace lexer my_lexer');
3.4刪除自定義參數
如果你不再需要某個自定義參數,你可以使用CTX_DDL.DROP_PREFERENCE刪除它。例如參數自定義參數my_lexer。

BEGIN
   ctx_ddl.drop_preference ('my_lexer');
END;
 
4管理DML操作
對於CTXSYS.CONTEXT索引,當應用程序對基表進行DML操作後,對基表的索引維護是必須的。索引維護包括索引同步和索引優化。
在索引建好後,我們可以在該用戶下查到Oracle自動產生了以下幾個表:(假設索引名爲myindex):
DR$myindex$IDR$myindex$KDR$myindex$RDR$myindex$N其中以I表最重要,可以查詢一下該表,看看有什麼內容:
SELECT token_text, token_count  FROM dr$i_rsk1$I  WHERE ROWNUM <= 20;
這裏就不列出查詢接過了。可以看到,該表中保存的其實就是Oracle 分析你的文檔後,生成的term記錄在這裏,包括term出現的位置、次數、hash值等。當文檔的內容改變後,可以想見這個I表的內容也應該相應改變,才能保證Oracle在做全文檢索時正確檢索到內容(因爲所謂全文檢索,其實核心就是查詢這個表)。那麼如何維護該表的內容呢?總不能每次數據改變都重新建立索引吧!這就用到sync 和 optimize了。

同步(sync): 將新的term 保存到I表;
優化(optimize): 清除I表的垃圾,主要是將已經被刪除的term從I表刪除。

 
 
當基表中的被索引文檔發生insert、update、delete操作的時候,基表的改變並不能馬上影響到索引上直到同步索引。可以查詢視圖CTX_USER_PENDING查看相應的改動。例如:

SELECT pnd_index_name, pnd_rowid,
       TO_CHAR (pnd_timestamp, 'dd-mon-yyyy hh24:mi:ss') timestamp
  FROM ctx_user_pending;

該語句的輸出類似如下:

PND_INDEX_NAME                 PND_ROWID          TIMESTAMP
------------------------------ ------------------ --------------------
MYINDEX                        AAADXnAABAAAS3SAAC 06-oct-1999 15:56:50
 
同步和優化方法: 可以使用Oracle提供的ctx_ddl包同步和優化索引。
一.     對於CTXCAT類型的索引來說,當對基表進行DML操作的時候,Oracle自動維護索引。對文檔的改變馬上反映到索引中。CTXCAT是事務形的索引。

 
4.1索引同步
4.1.1CTXSRV(同步進程)
 
Oracle提供一個全文索引同步服務進程負責監視索引表變動並且第一時間同步索引。
 
只需要在後臺運行這個進程,它會監視數據的變化,及時進行同步。但由於存在一些問題在未來的ORACLE版本中將要被取代。

啓動同步索引服務進程方法:

HOST ctxsrv -user ctxsys/ctxsys>&/tmp/ctx.log&
 
當你啓動了CTXSRV服務進程,在後臺的同步請求處理就會象事時一樣,在你提交修改12秒後新的數據馬上就被索引了。
 
與手工同步相比,自動索引同步更容易使索引變的稀疏,需要執行DBMS_JOB定期優化和重建索引rebuild parameters( 'sync' )。
 
默認情況下,如果你不啓動CTXSRV進程,索引不會自動更新除非你手工告訴它們去更新自己。你可以使用 alter index <iname> rebuild parameters ('sync') 更新索引。
 
ALTER INDEX search_idx  REBUILD parameters( 'sync' )
/
Index altered.
 
9i提供了新的專門用於更新索引的包ctx_ddl.sync_index(…)
 
4.1.2 CTX_DDL.SYNC_INDEX
在對基表插入,修改,刪除之後同步索引。推薦使用sync同步索引。
語法:
ctx_ddl.sync_index(
idx_name  IN  VARCHAR2 DEFAULT NULL
memory IN VARCHAR2 DEFAULT NULL,
part_name IN VARCHAR2 DEFAULT NULL
parallel_degree IN NUMBER DEFAULT 1);
 
idx_name   索引名稱
memory    指定同步索引需要的內存。默認是系統參數DEFAULT_INDEX_MEMORY 。
指定一個大的內存時候可以加快索引效率和查詢速度,且索引有較少的碎片
part_name  同步哪個分區索引。
parallel_degree  並行同步索引。設置並行度。

例如:
使用2M內存同步索引myindex:
 
BEGIN
   ctx_ddl.sync_index ('myindex', '2M');
END;
 
 
NOTE執行者必須是索引所有者或者CTXSYS用戶。如果執行者是CTXSYS用戶,索引名稱可以是空NULL,這樣默認優化全部的CONTEXT索引。這樣的同步效果就如同ctxsrv. 我們推薦定期執行作業job同步索引。-- 爲每一個索引制定單獨的作業job, 一個 ctxsys 作業job同步全部索引。這樣就減少了使用ctxsrv的機率,也不用在每次數據庫啓動後都要啓動CTXSRV服務進程。由於CTXSRV有一些缺陷,在未來將不再會被ORACLE使用或者被取代。
 
 
INSERT INTO mytable
     VALUES (2, 'first,second.this is the second rows before indexed');
COMMIT ;

EXEC ctx_ddl.sync_index('mytable_idx');

  SELECT /*+ FIRST_ROWS() */ ID, SCORE(1), TEXT
    FROM MYTABLE               
   WHERE CONTAINS (TEXT, 'searchterm', 1) > 0               
ORDER BY SCORE(1) DESC;
 
其中score(1)爲Oracle爲全文查詢計算的主題符合程度。
 
4.2索引優化
經常的索引同步將會導致你的CONTEXT索引產生碎片。索引碎片嚴重的影響了查詢的反應速度。你可以定期優化索引來減少碎片,減少索引大小,提高查詢效率。爲了更好的理解索引優化,我們先看看索引的結構以及碎片是如何產生的。
CONTEXT索引是反向索引,每一個索引項目都包括單詞和這個單詞所出現過的文檔地址。例如在一個初始化索引過程中,單詞DOG可以包括如下條目
DOG DOC1 DOC3 DOC5
當新的文檔被包含到表的時候,索引被同步。如果新行DOC7也包括單詞DOG,將會形成如下條目。
DOG DOC1 DOC3 DOC5
DOG DOC7
很多的DML操作以後,單詞DOG的條目可能如下情況:
DOG DOC1 DOC3 DOC5
DOG DOC7
DOG DOC9
DOG DOC11
同步新增加的文檔產生了索引碎片。單詞DOG的文擋列表會越來越長,索引越來越大。
你可以優化索引(CTX_DDL.OPTIMIZE_INDEX),使用FULL或者FAST參數都可以降低索引碎片,提高索引效率。
4.2.1文檔垃圾處理
當文本從表中刪除的時候,Oracle Text標記刪除的文檔,但是並不馬上修改索引。因此,就的文檔信息佔據了不必要的空間,導致了查詢額外的開銷。你必須以FULL模式優化索引,從索引中刪除無效的舊的信息。這個過程叫做垃圾處理。當你經常的對錶文本數據進行更新,刪除操作的時候,垃圾處理是很必要的。

BEGIN
   ctx_ddl.optimize_index ('myidx', 'full');
END;
4.2.2 Single Token Optimization
除了優化整個索引以外,你還可以專門對某個標記(token)進行優化。你可以僅僅優化那些經常查詢的標記(token,而不必花太多時間在很少查詢的單詞上。
例如,你可以專門優化token DOG,它經常被檢索或者經常被更新。這樣可以提高查詢這個token的查詢效率。
BEGIN
   ctx_ddl.optimize_index ('myidx', 'token', token => 'DOG');
END;
4.2.3 FAST MODE
這種方法僅僅使碎片行緊湊。但是,舊的數據並不從索引中刪除。

BEGIN
   ctx_ddl.optimize_index ('myidx', 'fast');
END;
二.     4.2.4使用job定時同步和優化
三.     用以下的兩個job來完成(job要建在和表同一個用戶下) :
-- sync:
VARIABLE jobno number;
BEGIN
DBMS_JOB.SUBMIT(:jobno,'ctx_ddl.sync_index(''myindex'');',
SYSDATE, 'SYSDATE + (1/24/4)');
commit;
END;
 
-- optimizer
VARIABLE jobno number;
BEGIN
DBMS_JOB.SUBMIT(:jobno,'ctx_ddl.optimize_index(''myindex'',''FULL'');',
SYSDATE, 'SYSDATE + 1');
commit;
四.    END;
其中, 第一個job的SYSDATE + (1/24/4)是指每隔15分鐘同步一次,第二個job的SYSDATE + 1是每隔1天做一次全優化。具體的時間間隔,你可以根據自己的應用的需要而定。至此,你的全文檢索功能已設置完成。
 
5 檢索 CONTAINS
你建立好CONTEXT索引後,可以使用CONTAINS操作符檢索數據了。你可以有很多查詢選項。你可以進行邏輯,模糊,通配符,主題等查詢,還可以查詢HTML和XML文檔。
 
5.1簡單例子

SQL:

SELECT語句中,可以在WHERE指定CONTAINS操作符。還可以指定返回記錄的得分(SCORE)。
SELECT score (1) title
  FROM news
 WHERE contains (text, 'Oracle', 1) > 0;

得分SCORE是指查詢結果的貼切程度。得分越高表示查詢信息滿意度越高。你可以根據SCORE進行排序。
 
SELECT score (1), title
    FROM news
   WHERE contains (text, 'Oracle', 1) > 0
ORDER BY score (1) DESC;
 
SELECT score (1), title, issue_date
    FROM news
   WHERE contains (text, 'Oracle', 1) > 0
     AND issue_date >= ('01-OCT-97')
ORDER BY score (1) DESC;
A structured query, also called a mixed queryFor example, the upper SELECT statement returns all articles that contain the word Oracle that were written on or after October 1, 1997.
SELECT id,score(1),score (2),score (1)+ score (2) total, text
    FROM mytable
   WHERE contains (text, 'biti', 1) > 0
      OR contains (text, 'hello', 2) > 0
ORDER BY total DESC;
根據contains中的不同數字標示各個contains返回的分數Score
 
PL/SQL形式:
DECLARE
   rowno   NUMBER := 0;
BEGIN
   FOR c1 IN  (  SELECT score (1) score, title
                   FROM news
                  WHERE contains (text, 'Oracle', 1) > 0
               ORDER BY score (1) DESC)
   LOOP
      rowno :=   rowno + 1;
      DBMS_OUTPUT.put_line ( c1.title || ': ' || c1.score);
      EXIT WHEN rowno = 10;
   END LOOP;
END;
 
返回了得分最高的前10條記錄.

5.2 Logical Operators
允許你組合搜索條件,通過使用AND,OR等邏輯。
操作符
符號
描述
例子表達式
AND
&
Use the AND operator to search for documents that contain at least one occurrence of each of the query terms.
Score returned is the minimum of the operands.
'cats AND dogs'
'cats & dogs'
OR
|
Use the OR operator to search for documents that contain at least one occurrence of any of the query terms.
Score returned is the maximum of the operands.
'cats | dogs'
'cats OR dogs'
 
NOT
~
Use the NOT operator to search for documents that contain one query term and not another.
To obtain the documents that contain the term animals but not dogs, use the following expression:
'animals ~ dogs'
ACCUM
,
Use the ACCUM operator to search for documents that contain at least one occurrence of any of the query terms. The accumulate operator ranks documents according to the total term weight of a document.
The following query returns all documents that contain the terms dogs, cats and puppies giving the highest scores to the documents that contain all three terms:
'dogs, cats, puppies'
EQUIV
=
Use the EQUIV operator to specify an acceptable substitution for a word in a query.
The following example returns all documents that contain either the phrase alsatians are big dogs or German shepherds are big dogs:
'German
shepherds=alsatians are
big dogs'

Oracle Text ReferenceRelease 9.2可以查看更多的選項。


5.3
如何優化查詢
5.3.1收集統計信息
By collecting statistics on the Text domain index, the Oracle cost-based optimizer is able to do the following:
  • estimate the selectivity of the CONTAINS predicate
  • estimate the I/O and CPU costs of using the Text index, that is, the cost of processing the CONTAINS predicate using the domain index
  • estimate the I/O and CPU costs of each invocation of CONTAINS
5.3.2使用 FIRST_ROWS(n) for ORDER BY Queries
With the FIRST_ROWS hint, Oracle instructs the Text index to return rowids in score-sorted order, if possible.
Without the hint, Oracle sorts the rowids after the Text index has returned all the rows in unsorted order that satisfy the CONTAINS predicate. Retrieving the entire result set as such takes time.
Since only the first 10 hits are needed in this query, using the hint results in better performance.
 
Note:Use the FIRST_ROWS(n) hint when you need only the first few hits of a query. When you need the entire result set, do not use this hint as it might result in poor performance.
 
 
 
6.分區
 
CONTEXT索引支持在分區表山建立本地分區索引,但仍然有一些限制。如下:
 
l         分區表必須是按範圍分區,不支持HASH分區和複合分區。
l         你應該爲每個分區指定索引分區名稱。如果不指定,系統默認爲每個分區表指定。分區索引列表的順序必須和分區表的順序一致。
l         可以爲每個分區分別指定PRAMETERS參數,但是PARAMETERS只允許包括STORAGE和MEMEROY參數。
l         不能指定ONLINE建立索引。

可以查詢CTX_INDEX_PARTITIONS或者CTX_USER_INDEX_PARTITIONS獲得更多信息。

6.1創建一個本地分區索引:

------------------------BEGIN---------------------------
PROMPT create partitioned table and populate it
 
CREATE TABLE part_tab (a int, b varchar2(40)) PARTITION BY RANGE(a)
(partition p_tab1 values less than (10),
 partition p_tab2 values less than (20),
 partition p_tab3 values less than (30));
 
PROMPT create customer storage preference assigned each partition
 
Execute ctx_ddl.drop_preference('mystore1');
BEGIN
   ctx_ddl.create_preference ('mystore1', 'BASIC_STORAGE');
   ctx_ddl.set_attribute ('mystore1', 'I_TABLE_CLAUSE', 'tablespace indx ');
   ctx_ddl.set_attribute (
      'mystore1',
      'I_INDEX_CLAUSE',
      'tablespace users03 compress 2 '
   );
END;
 
PROMPT create partitioned index
 
CREATE INDEX part_idx on part_tab(b) INDEXTYPE IS CTXSYS.CONTEXT
LOCAL (partition p_idx1 parameters(‘storage mystore1’), partition p_idx2 parameters(‘storage mystore2’), partition p_idx3 parameters(‘storage mystore3’));
 
-------------------------END----------------------------

 
6.2 並行的創建一個本地分區索引

可以並行的建立分區索引,加快建立索引速度。但是建立索引不能“一步到位“。我們必須先建立一個unusable索引,然後利DBMS_PCLXUTIL.BUILD_PART_INDEX 並行建立索引。

------------------------BEGIN---------------------------

PROMPT the base table has three partitions.
PROMPT  We create a local partitioned unusable index first
 
CREATE INDEX tdrbip02bx ON tdrbip02b(text)
indextype is ctxsys.context local (partition tdrbip02bx1,
                                   partition tdrbip02bx2,
                                   partition tdrbip02bx3)
unusable;
 
PROMPT run the DBMS_PCLUTIL.BUILD_PART_INDEX,which builds the 3 partitions in parallel (inter-partition parallelism). Also inside each partition, index creation is done in parallel (intra-partition parallelism) with a parallel degree of 2.
 
BEGIN
   DBMS_PCLXUTIL.build_part_index (3, 2, 'TDRBIP02B', 'TDRBIP02BX', TRUE);
END;

-------------------------END----------------------------

 
6.3查詢分區索引

你可以在單個表分區上進行全文檢索。

SELECT *
    FROM part_tab PARTITION (p_tab4)
   WHERE contains (b, 'Oracle') > 0
ORDER BY score;
7Oracle Text支持對本地文件的檢索
它的實現是依靠參數datastore和filter的組合。在數據庫的文本列中只保存指向硬盤文件的指針。建立索引的時候,Oracle讀取硬盤上的文件並且將索引存儲在Oracle數據庫中。
 
Oracle支持對很多格式的文件的文本檢索,包括文本文件Txt,  Html文件,      Word文檔,  Excel表格,  PowerPoint 的文本檢索,也支持PDF(pdf版本1.4目前還不支持)。
 
而且配合Lexer參數很好的支持了中文字符集的檢索。
   
具體實現方法:
首先,建立存儲選項參數。制定DATASTORE參數爲FILE_DATASTROE,提示Oracle從文件路徑中索引文本。
然後制定path參數,你可以指定多個文件存儲的文件路徑 ,windows環境用”;”號間隔(Path1;Path2;Path3;;), Unix環境用:號間隔(Path1:Path2:Path3::)

BEGIN
   ctx_ddl.create_preference ('my_datastore_prefs', 'FILE_DATASTORE');
   ctx_ddl.set_attribute ('my_datastore_prefs', 'path', 'c:/TEMP');
END;

下一步,建立保存這些文件名稱的表。Id列是主鍵,title列是對文本的簡單說明,thefile列保存着磁盤中Path目錄下文件的名稱。文件必須能夠在Path路徑下找到,否則Oracle會報文件無法訪問的錯誤信息。

然後向表中插入數據,注意:thefile列保存的必須是服務器上的指定的Path路徑下面的文件。
 
CREATE TABLE mydocs( id NUMBER PRIMARY KEY, title VARCHAR2(255), thefile 
VARCHAR2(255) );
 
INSERT INTO mydocs( id, title, thefile ) VALUES( 1, 'Document1', 'WordDoc1.doc');
INSERT INTO mydocs( id, title, thefile ) VALUES( 2, 'Document2', 'WordDoc2.doc');
INSERT INTO mydocs( id, title, thefile ) VALUES( 3, 'Document3', 'WordDoc3.doc');
COMMIT;
 
建立全文索引,使用參數Datastore 和Filter,Lexer 。 Filter可以幫助Oracle識別不同格式文件,可以是文本文件,Word文檔,Pdf文檔等。Lexer用來保證可以很好的從文件中索引中文信息。
 
CREATE INDEX mydocs_text_index ON mydocs(thefile) INDEXTYPE IS ctxsys.context
    PARAMETERS('datastore my_datastore_prefs Filter ctxsys.info_filter Lexer my_lexer');
 
--
-- 測試是否索引文件成功
-- 
SELECT id,title
  FROM mydocs
 WHERE contains( thefile, '你好' ) > 0;
 
 
指定路徑帶來的相關問題:
CTX_DDL.SET_ATTRIBUTE( 'my_datastore_prefs', 'path', 'c:/TEMP;c:/docs' );
如果在2個目錄中均有同名的文件1.doc,如果在thefile列中保存的僅僅是文件名稱 1.doc,則Oracle順序查找路徑下的文件,這樣就會索引2次在C:/TEMP下的文件1.doc. 我們可以通過加上文件的路徑信息。
 
在維護文檔修改的時候同步索引的問題:
如果你修改了路徑下面的某個文件的內容,加入了文本或者刪除了文本,Oracle在同步的時候不會察覺到文檔的內容的修改。有一個方法可以保證同步:
 
修改了內容之後,更新一下表thefile的信息,但仍保證文本路徑不變。
 
UPDATE mydocs
   SET thefile = 'c:/source.doc'
 WHERE thefile = 'c:/source.dco';
 
再次執行同步索引的時候,Oracle纔會保持文檔內容同步。
 
關於建立以及同步索引的時候發生的錯誤信息可以從ctx_user_index_errors用戶視圖中查看。
 
8Oracle Text 支持檢索對網頁的文本檢索
通過在表裏面存儲網絡上各種格式的文本文件,HTML文件的路徑UrlOracle在建立索引的時候,可以順着Url讀取文件的流信息,並且將索引存儲在磁盤上。這樣通過本地查找索引可以獲得有用的網頁的Url
通過自定義Datastore選項,指定URL_DATASTORE類型。它支持Http訪問,和Ftp訪問,本地文件系統的訪問。
存儲在文本列中的Url格式如下:
 
[URL:]<access_scheme>://<host_name>[:<port_number>]/[<url_path>]
access_scheme 字符串可以是ftp http或者file. 例如:
http://mymachine.us.Oracle.com/home.html

―――――――――――――――――――――――――――
注:
login:password@ 格式的語法只有在Ftp訪問形式下才有效
―――――――――――――――――――――――――――
URL_DATASTORE 參數
URL_DATASTORE的一些參數,其中timeoutproxy是經常用到的:
屬性
屬性值
timeout
Specify the timeout in seconds. The valid range is 15 to 3600 seconds. The default is 30.這個參數根據網絡性能調整。
maxthreads
Specify the maximum number of threads that can be running simultaneously. Use a number between 1and 1024. The default is 8.
Urlsize
Specify the maximum length of URL string in bytes. Use a number between 32 and 65535. The default is 256.
maxurls
Specify maximum size of URL buffer. Use a number between 32 and 65535. The defaults is 256.
maxdocsize
Specify the maximum document size. Use a number between 256 and 2,147,483,647 bytes (2 gigabytes). The defaults is 2,000,000.
http_proxy
Specify the host name of http proxy server. Optionally specify port number with a colon in the form hostname:port
ftp_proxy
Specify the host name of ftp proxy server. Optionally specify port number with a colon in the form hostname:port.
no_proxy
Specify the domain for no proxy server. Use a comma separated string of up to 16 domain names.
 
 
索引建立過程:
首先建立自己的URL_DATASTORE選項。如下指定了代理,Timeout時間。

BEGIN
   ctx_ddl.create_preference ('URL_PREF', 'URL_DATASTORE');
   ctx_ddl.set_attribute ('URL_PREF', 'Timeout', '300');
END;

建立存儲Url路徑的表:

CREATE TABLE urls(id NUMBER PRIMARY KEY, url VARCHAR2(2000));
INSERT INTO urls
     VALUES (1, 'http:// http://intranet-center/');
INSERT INTO urls
     VALUES (2, 'http://founderweb:9080/default.jsp');
COMMIT ;
 
建立索引,索引Html文件可以使用HTML_SECTION_GROUP:

CREATE INDEX datastores_text ON urls ( url )  
INDEXTYPE IS ctxsys.CONTEXT   PARAMETERS (
 'Datastore URL_PREF Lexer my_lexer Section group ctxsys.HTML_SECTION_GROUP' );

SELECT token_text
  FROM dr$datastore_text$i;
 
關於建立以及同步索引的時候發生的錯誤信息可以從ctx_user_index_errors用戶視圖中查看。

9. 常見錯誤
下面就一些常見的錯誤信息給出解釋和解決辦法:
 
1sync 失敗
 
DRG-10595: ALTER INDEX T_DOC6_CT失敗
DRG-50857: Oracle error in drsxsopen
ORA-01480: STR 賦值變量缺少空後綴
 
解決:這是8i的一個bug, 但可以避免它,方法是在同步之前先發一個語句:
 
ALTER SESSION SET nls_language=american;
 
2create index 失敗

ORA-29855: 執行 ODCIINDEXCREATE 例行程序時出錯
ORA-20000: interMedia Text 錯誤:
ORA-06512: 在"CTXSYS.DRUE", line 126
ORA-06512: 在"CTXSYS.TEXTINDEXMETHODS", line 54
ORA-06512: 在line 1
 
解決:這是8.1.6.3之前的版本的一個bug, 在處理中文時,某個特殊字符造成的。向Oracle索取補丁,或者自己去metalink.Oracle.com 下載(需要CSI 號碼)。
 
3create index 失敗
 
RA-29855: 執行 ODCIINDEXCREATE 例行程序時出錯
ORA-20000: interMedia Text 錯誤:
DRG-50704: Net8 監聽器沒有運行或無法啓動外部過程
ORA-28575: 無法打開與外部過程代理程序的 RPC 連接
ORA-06512: 在"CTXSYS.DRUE", line 126
ORA-06512: 在"CTXSYS.TEXTINDEXMETHODS", line 54
ORA-06512: 在line 1
 
解決:明顯的extproc配置不當。仔細閱讀本文基本設置的第二步。
 
4.訪問建有索引的表時失敗
 
ora-29861: 域索引標記爲loading/failed/unusable
 
解決:這是該表的一個intermedia索引有問題,該索引要麼沒有正確建立,要麼是某次同步失敗導致它狀態異常。先查到是哪個索引:

SELECT idx_name, idx_status
  FROM ctxsys.ctx_indexes;

然後同步該索引或者強制刪除它:
 
重建:ALTER INDEX myindex REBUILD ONLINE PARAMETERS('sync');
刪除:DROP INDEX myindex FORCE;
 
5.使用chinese_lexer失敗

ERROR at row 1:
ORA-29855: err on ODCIINDEXCREATE 
ORA-20000: interMedia Text err:
DRG-10502: index 1386 is not existing.
DRG-11102: the lexer cann't analyze as SIMPLIFIED CHINESE_CHINA.ZHS16GBK
ORA-06512: 在"CTXSYS.DRUE", line 126
ORA-06512: 在"CTXSYS.TEXTINDEXMETHODS", line 54
ORA-06512: 在line 1

解決:chinese_lexer 只支持utf8字符集。現在你面臨抉擇:忍受chinese vgram lexer的愚蠢,或者將數據庫字符集改到 utf8, 但面對可能引起你的應用不能正確處理中文的風險(先諮詢Oracle support, 並且與你的應用軟件提供商聯繫)。
 
6.升級或應用patch後失敗

ORA-29856: err when execute ODCIINDEXDROP
ORA-20000: interMedia Texterr
ORA-06508: PL/SQL: can not find program unit beingcalled
ORA-06512: at "CTXSYS.DRUE", line 126
ORA-06512: at"CTXSYS.TEXTINDEXMETHODS", line 229
ORA-06512: at line 1

解決:這是intermedia的某個object 沒有正確產生或者編譯。用ctxsys用戶登錄後,運行:
$Oracle_home/ctx/admin/dr0pkh.sql

$Oracle_home/ctx/admin/dr0plb.sql
以重新產生所有的package.你也可以直接察看dba_objects視圖,找出那些屬於ctxsys用戶並且status爲invalid的東西,重新產生或者重新編譯。(你可能會發現有許多這種東西,不要驚訝,Oracle不會因此而崩潰)。
 
7create index 失敗
ERROR 位於第 1 行:
ORA-29855: 執行 ODCIINDEXCREATE 例行程序時出錯
ORA-20000: interMedia Text 錯誤:
DRG-50857: Oracle error in driddl.IndexResume
ORA-04030: 在嘗試分配 524288 字節 (cursor work he,QERHJ Bit vector)時進程內存不足
ORA-06512: 在"CTXSYS.DRUE", line 126
ORA-06512: 在"CTXSYS.TEXTINDEXMETHODS", line 214
ORA-06512: 在line 1

解決:引起這個問題可以有多種原因,首先你可以將sort_area_size這個參數減小到不多於2M,這可以防止Oracle在創建索引時分配太多的sort 內存而耗盡資源。 但如果這不起作用,而且你是8.1.7, 則恭喜,你hit 了bug 1391737. 該bug 在你要建索引的字段,如果某條記錄的長度超過2000字符時引起Oracle耗盡內存資源。別無它法,除了打 8.1.7.1B 的補丁。
 
10.附錄

10.1  ORACLE TEXT
資源:

 
10.2 關於索引性能的FAQ
This section answers some of the frequently asked questions about indexing performance.
How long should indexing take?
Answer: Indexing text is a resource-intensive process. Obviously, the speed of indexing will depend on the power of the hardware involved.
As a benchmark, with an average document size of 5K, Oracle Text can index approximately 200 documents per second with the following hardware and parallel configuration:
  • 4x400Mhz Sun Sparc CPUs
  • 4 gig of RAM
  • EMC symmetrix (24 disks striped)
  • Parallel degree of 5 with 5 partitions
  • Index memory of 600MB per index process
  • XML news documents that averaged 5K in size
  • USER_DATASTORE
Other factors such as your document format, location of your data, and the calls to user-defined datastores, filters, and lexers can have an impact on your indexing speed.
Which index memory settings should I use?
Answer: You can set your index memory with the system parameters DEFAULT_INDEX_MEMORY and MAX_INDEX_MEMORY. You can also set your index memory at run time with the CREATE INDEX memory parameter in the parameter string.
You should aim to set the DEFAULT_INDEX_MEMORY value as high as possible, without causing paging.
You can also improve Indexing performance by increasing the SORT_AREA_SIZE system parameter.
Experience has shown that using a large index memory setting, even into hundreds of megabytes, will improve the speed of indexing and reduce the fragmentation of the final indexes. However, if set too high, then the memory paging that occurs will cripple indexing speed.
With parallel indexing, each stream requires its own index memory. When dealing with very large tables, you can tune your database system global area (SGA) differently for indexing and retrieval. For querying, you are hoping to get as much information cached in the system global area's (SGA) block buffer cache as possible. So you should be allocating a large amount of memory to the block buffer cache. But this will not make any difference to indexing, so you would be better off reducing the size of the SGA to make more room for a large index memory settings during indexing.
You set the size of SGA in your Oracle initialization file.
See Also:
Oracle Text Reference to learn more about Oracle Text system parameters.
Oracle9i Database Administrator's Guide for more information on setting SGA related parameters.
Oracle9i Database Performance Guide and Reference for more information on memory allocation and setting the SORT_AREA_SIZE parameter.
How much disk overhead will indexing require?
Answer: The overhead, the amount of space needed for the index tables, varies between about 50% of the original text volume and 200%. Generally, the larger the total amount of text, the smaller the overhead, but many small records will use more overhead than fewer large records. Also, clean data (such as published text) will require less overhead than dirty data such as emails or discussion notes, since the dirty data is likely to include many unique words from mis-spellings and abbreviations.
A text-only index is smaller than a combined text and theme index. A prefix and substring index makes the index significantly larger.
How does the format of my data affect indexing?
Answer: You can expect much lower storage overhead for formatted documents such as Microsoft Word files since such documents tend to be very large compared to the actual text held in them. So 1GB of Word documents might only require 50MB of index space, whereas 1GB of plain text might require 500MB, since there is ten times as much plain text in the latter set.
Indexing time is less clear-cut. Although the reduction in the amount of text to be indexed will have an obvious effect, you must balance this out against the cost of filtering the documents with the INSO filter or other user-defined filters.
Can I index in parallel?
Answer: Yes, you can index in parallel. Parallel indexing can improve index performance when you have a large amount of data, and have multiple CPUs.
You use the PARALLEL keyword when creating the index:
CREATE INDEX index_name ON table_name (column_name)
INDEXTYPE IS ctxsys.context PARAMETERS ('...') PARALLEL 3;
 
This will create the index with up to three separate indexing processes depending on your resources.

Note:
It is no longer necessary to create a partitioned table to index in parallel as was the case in earlier releases.

 

Note:
When you create a local index in parallel as such (which is actually run in serial), subsequent queries are processed in parallel by default. Creating a non-partitioned index in parallel does not turn on parallel query processing.
Parallel querying degrades query throughput especially on heavily loaded systems. Because of this, Oracle recommends that you disable parallel querying after indexing. To do so, use ALTER INDEX NOPARALLEL.


How do I create a local partitioned index in parallel?
Answer: You can improve indexing performance by creating a local index in parallel.
However, currently you cannot create a local partitioned index in parallel using the PARALLEL parameter with CREATE INDEX. In such cases the parameter is ignored and indexing proceeds serially.
To create a local index in parallel, create an unusable index first, then run the DBMS_PCLXUTIL.BUILD_PART_INDEX utility.
In this example, the base table has three partitions. We create a local partitioned unusable index first, the run the DBMS_PCLUTIL.BUILD_PART_INDEX, which builds the 3 partitions in parallel (inter-partition parallelism). Also inside each partition, index creation is done in parallel (intra-partition parallelism) with a parallel degree of 2.
CREATE INDEX tdrbip02bx ON tdrbip02b(text)
indextype is ctxsys.context local (partition tdrbip02bx1,
                                   partition tdrbip02bx2,
                                   partition tdrbip02bx3)
unusable;
 
exec dbms_pclxutil.build_part_index(3,2,'TDRBIP02B','TDRBIP02BX',TRUE);
How can I tell how far my indexing has got?
Answer: You can use the CTX_OUTPUT.START_LOG procedure to log output from the indexing process. Filename will normally be written to $ORACLE_HOME/ctx/log, but you can change the directory using the LOG_DIRECTORY parameter in CTX_ADM.SET_PARAMETER.
 
See Also:
Oracle Text Reference to learn more about using this procedure.
 
 
8.3Frequently Asked Questions About Updating the Index
 
This section answers some of the frequently asked questions about updating your index and related performance issues.
How often should I index new or updated records?
Answer: How often do you need to? The less often you run reindexing with CTX_DLL.SYNC_INDEX then the less fragmented your indexes will be, and the less you will need to optimize them.
However, this means that your data will become progressively more out of date, which may be unacceptable for your users.
Many systems are OK with overnight indexing. This means data that is less than a day old is not searchable. Other systems use hourly, ten minute, or five minute updates.
See Also:
Oracle Text Reference to learn more about using CTX_DDL.SYNC_INDEX.
How can I tell when my indexes are getting fragmented?
Answer: The best way is to time some queries, run index optimization, then time the same queries (restarting the database to clear the SGA each time, of course). If the queries speed up significantly, then optimization was worthwhile. If they don't, you can wait longer next time.
You can also use CTX_REPORT.INDEX_STATS to analyze index fragmentation.
See Also:
Oracle Text Reference to learn more about using the CTX_REPORT package.
Does memory allocation affect index synchronization?
Answer: Yes, the same way as for normal indexing. But of course, there are often far fewer records to be indexed during a synchronize operation, so it is not usually necessary to provide hundreds of megabytes of indexing memory
 
104示例JSP代碼
This section describes the JSP web application.
Web Application Prerequisites
This application has the following requirements:
·          Your Oracle database (version 8.1.6 or higher) is up and running.
·          You have a web server such as Apache up and running and correctly configured to send requests to the Oracle9i server.
JSP Sample Code: search_html.jsp
 
 
<%@ page import="java.sql.* , Oracle.jsp.dbutil.*" %>
<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:@localhost:1521:betadev";
 
  java.util.Properties info = new java.util.Properties();
 
  Connection conn = null;
  ResultSet  rset = null;
  Statement  stmt = null;
 
 
    if (name.isEmpty()) { %>
 
      <html>
       <title>search1 Search</title>
       <body>
       <center>
         <form method=post>
         Search for:
         <input type=text name=query size=30>
         <input type=submit value="Search">
         </form>
       </center>
       <hr>
     </body>
     </html>
 
   <%
   }
   else {
   %>
 
    <html>
       <title>Search</title>
       <body>
       <center>
         <form method=post action="search_html.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", "ctxdemo");
       info.put ("password","ctxdemo");
       conn = DriverManager.getConnection(connStr,info);
 
       stmt = conn.createStatement();
       String theQuery =   request.getParameter("query");
 
       String myQuery = "select /*+ FIRST_ROWS */ rowid, tk, title,  score(1) 
scr from search_table where contains(text, '"+theQuery+"',1 ) > 0 order by 
score(1) desc";
       rset = stmt.executeQuery(myQuery);
 
       String color = "ffffff";
       int myTk = 0;
       String myTitle = null;
       int myScore = 0;
       int items = 0;
       while (rset.next()) {
         myTk = (int)rset.getInt(2);
         myTitle = (String)rset.getString(3);
         myScore = (int)rset.getInt(4);
         items++;
 
         if (items == 1) {
    %>
 
            <center>
               <table border="0">
                  <tr bgcolor="#6699CC">
                    <th>Score</th>
                    <th>Title</th>
                  </tr>
    <%   } %> 
 
         <tr bgcolor="#<%= color %>">
           <td> <%= myScore %>%</td>
           <td> <%= myTitle %>
           </td>
         </tr>
 
   <%
         if (color.compareTo("ffffff") == 0)
               color = "eeeeee";
             else
               color = "ffffff";
 
 
       }
     } 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>
     </body></html>
     <%
  }

 
(END)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章