(1)数据存储逻辑搜索表的所有行,并读取列中的数据。通常,这只是列数据,但有些数据存储使用列数据作为文档数据的指针。例如,URL_DATASTORE将列数据作为URL 使用。
(2)过滤器提取文档数据并将其转换为文本表示方式。存储二进制文档(如Word 或Acrobat 文件)时需要这样做。过滤器的输出不必是纯文本格式-- 它可以是XML 或HTML 之类的文本格式。
(3)分段器提取过滤器的输出信息,并将其转换为纯文本。包括XML 和HTML 在内的不同文本格式有不同的分段器。转换为纯文本涉及检测重要文档段标记、移去不可见的信息和文本重新格式化。
(4)词法分析器提取分段器中的纯文本,并将其拆分为不连续的标记。既存在空白字符分隔语言使用的词法分析器,也存在分段复杂的亚洲语言使用的专门词法分析器。
(5)索引引擎提取词法分析器中的所有标记、文档段在分段器中的偏移量以及被称为非索引字的低信息含量字列表,并构建反向索引。倒排索引存储标记和含有这些标记的文档。
步骤:
1)建立并装载文本 (包含带有需要检索的文本字段)
装载方法主要有以下几种:
(1)SQL INSERT 语句
(2)ctxload 可执行文件
(3)SQL*Loader
(4)从 BFILE 中装载 LOB 的DBMS_LOB.LOADFROMFILE() PL/SQL 过程
(5)Oracle Call Interface
(2)配置索引
(3)建立索引
文本装入文本列后,就可以创建Oracle Text索引。文档以许多不同方案、格式和语言存储。因此,每个Oracle Text 索引有许多需要设置的选项,以针对特定情况配置索引。创建索引时,OracleText可使用若干个默认值,但在大多数情况下要求用户通过指定首选项来配置索引。
每个索引的许多选项组成功能组,称为"类",每个类集中体现配置的某一方面,可以认为这些类就是与文档数据库有关的一些问题。例如:数据存储、过滤器、词法分析器、相关词表、存储等。
每个类具有许多预定义的行为,称之为对象。每个对象是类问题可能具有的答案,并且大多数对象都包含有属性。通过属性来定制对象,从而使对索引的配置更加多变以适应于不同的应用。
(1)存储(Storage)类
存储类指定构成OracleText索引的数据库表和索引的表空间参数和创建参数。它仅有一个基本对象:BASIC_STORAGE,其属性包括:I_Index_Clause、I_Table_Clause、K_Table_Clause、N_Table_Clause、P_Table_Clause、R_Table_Clause。
(2)数据存储(Datastore)类
数据存储:关于列中存储文本的位置和其他信息。默认情况下,文本直接存储到列中,表中的每行都表示一个单独的完整文档。其他数据存储位置包括存储在单独文件中或以其URL 标识的Web 页上。七个基本对象包括:Default_Datastore、Detail_Datastore、Direct_Datastore、File_Datastore、Multi_Column_Datastore、URL_Datastore、User_Datastore,。
(3)文档段组(Section Group)类
文档段组是用于指定一组文档段的对象。必须先定义文档段,然后才能使用索引通过WITHIN 运算符在文档段内进行查询。文档段定义为文档段组的一部分。包含七个基本对象:AUTO_SECTION_GROUP、BASIC_SECTION_GROUP、HTML_SECTION_GROUP、NEWS_SECTION_GROUP、NULL_SECTION_GROUP、XML_SECTION_GROUP、PATH_SECTION_GROUP。
(4)相关词表(Wordlist)类
相关词表标识用于索引的词干和模糊匹配查询选项的语言,只有一个基本对象BASIC_WORDLIST,其属性有:Fuzzy_Match、Fuzzy_Numresults、Fuzzy_Score、Stemmer、Substring_Index、Wildcard_Maxterms、Prefix_Index、Prefix_Max_Length、Prefix_Min_Length。
(5)索引集(Index Set)
索引集是一个或多个Oracle索引(不是OracleText索引)的集合,用于创建CTXCAT类型的OracleText索引,只有一个基本对象BASIC_INDEX_SET。
(6)词法分析器(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 ZHT16MSWIN950ZHT16HKSCS UTF8 该分析器按字为单元来分析汉语句子。‘中国人民站起来了’这句话,会被它分析成如下几个term: ‘中’,‘中国’,‘国人’,‘人民’,‘民站’,‘站起’,起来’,‘来了’,‘了’。可以看出,这种分析方法,实现算法很简单,并且能实现‘一网打尽’,但效率则是差强人意。
n chinese_lexer: 这是一个新的汉语分析器,只支持utf8字符集。上面已经看到,chinese vgram lexer这个分析器由于不认识常用的汉语词汇,因此分析的单元非常机械,像上面的‘民站’,‘站起’在汉语中根本不会单独出现,因此这种term是没有意义的,反而影响效率。chinese_lexer的最大改进就是该分析器能认识大部分常用汉语词汇,因此能更有效率地分析句子,像以上两个愚蠢的单元将不会再出现,极大 提高了效率。但是它只支持utf8, 如果你的数据库是zhs16gbk字符集,则只能使用笨笨的那个Chinese vgram lexer.
(7)过滤器(Filter)类
过滤器确定如何过滤文本以建立索引。可以使用过滤器对文字处理器处理的文档、格式化的文档、纯文本和HTML 文档建立索引,包括5个基本对象:CHARSET_FILTER、INSO_FILTER INSO、NULL_FILTER、PROCEDURE_FILTER、USER_FILTER。
(8)非索引字表(Stoplist)类
非索引字表类是用以指定一组不编入索引的单词(称为非索引字)。有两个基本对象:BASIC_STOPLIST(一种语言中的所有非索引字)、MULTI_STOPLIST (包含多种语言中的非索引字的多语言非索引字表)。
(4)发出查询
1 词查询示例
词查询是对输入到CONTAINS 运算符中单引号间的精确单词或短语的查询。在以下示例中,我们将查找文本列中包含oracle 一词的所有文档。每行的分值由使用标签1 的SCORE 运算符选定:
SELECT SCORE(1) title from news WHERE CONTAINS(text, 'oracle', 1) > 0;
在查询表达式中,可以使用 AND 和 OR 等文本运算符来获取不同结果。还可以将结构性谓词添加到WHERE 子句中。可以使用count(*)、CTX_QUERY.COUNT_HITS或CTX_QUERY.EXPLAIN 来计算查询的命中 (匹配) 数目。
2 ABOUT查询示例
在所有语言中,ABOUT查询增加了某查询所返回的相关文档的数目。在英语中,ABOUT 查询可以使用索引的主题词组件,该组件在默认情况下创建。这样,运算符将根据查询的概念返回文档,而不是仅依据所指定的精确单词或短语。例如,以下查询将查找文本列中关于主题politics 的所有文档,而不是仅包含politics 一词的文档:
SELECT SCORE(1) title from news WHERE CONTAINS(text, 'about(politics)', 1) > 0;
显示满足查询条件的文档
通常,通过使用Oracle Text查询应用程序,用户可查看查询所返回的文档。用户从命中列表中选择一个文档,然后应用程序以某种形式显示该文档。通过OracleText,可以用不同的方式再现文档。例如,可以通过突出显示查询词来显示文档。突出显示的查询词可以是相关词查询中的词,也可以是英文ABOUT 查询中的主题词。
以下是关于输出效果和用于每个输出效果的过程的信息:
突出显示的文档,纯文本格式版本(CTX_DOC.MARKUP)
突出显示的文档,HTML版本(CTX_DOC.MARKUP)
突出显示HTML版本的偏移量信息(CTX_DOC.HIGHLIGHT)
纯文本格式版本,无突出显示(CTX_DOC.FILTER)
HTML版本文档,无突出显示(CTX_DOC.FILTER)
(5)索引维护:同步与优化
索引建好后,如果表中的数据发生变化,比如增加或修改了记录,怎么办?由于对表所发生的任何DML语句,都不会自动修改索引,因此,必须定时同步(sync)和优化(optimize)索引,以正确反映数据的变化。
在索引建好后,可以在该用户下查到Oracle自动产生了以下几个表:(假设索引名为myindex):
DR$myindex$I,DR$myindex$K,DR$myindex$R,DR$myindex$N
其中以I表最重要,可以查询一下该表:
select token_text, token_count from DR$ myindex $I where rownum<=20;
查询结果在此省略。可以看到,该表中保存的其实就是Oracle 分析你的文档后,生成的term记录在这里,包括term出现的位置、次数、hash值等。当文档的内容改变后,可以想见这个I表的内容也应该相应改变,才能保证Oracle在做全文检索时正确检索到内容(因为所谓全文检索,其实核心就是查询这个表)。那么如何维护该表的内容,不能每次数据改变都重新建立索引,这就要用到sync 和 optimize了。
同步(sync):将新的term 保存到I表;
优化(optimize):清除I表的垃圾,主要是将已经被删除的term从I表删除。
Oracle提供了一个所谓的ctx server来做这个同步和优化的工作,只需要在后台运行这个进程,它会监视数据的变化,及时进行同步。另外,也可以用以下的job来完成(该job要建在和表同一个用户下)
具体操作举例:
测试用户名: user1 有超级用户权限
表: docs
drop table docs;
CREATE TABLE docs (id NUMBER PRIMARY KEY, text VArCHAR2(80));
--1.建表并装载文本 类型 VARCHAR2、CLOB、BLOB、CHAR或BFILE
INSERT INTO docs VALUES (1,'the first doc');
INSERT INTO docs VALUES (2,'the second doc');
INSERT INTO docs VALUES (3,'我是中国人 你是谁');
INSERT INTO docs VALUES (4,'大坏蛋');
COMMIT;
ALTER USER ctxsys ACCOUNT UNLOCK;
alter user ctxsys identified by ctxsys;
-- 在ctxsys用户下把ctx_ddl的执行权限赋于要使用oracle全文索引的用户
grant execute on ctx_ddl to user1;
--2.配置索引
-- 建立分词器
exec ctx_ddl.create_preference ('my_lexer', 'chinese_vgram_lexer');
--3. 建立索引
CREATE INDEX ctx_idx_text ON docs(text) indextype is ctxsys.context parameters('lexer my_lexer');
--4.测试
SELECT id FROM docs WHERE CONTAINS (text, '中国') > 0;
--5.索引维护
create or replace procedure sync
is
begin
execute immediate
'alter index ctx_idx_text rebuild online' ||
' parameters ( ''sync'' )' ;
execute immediate
'alter index ctx_idx_text rebuild online' ||
' parameters ( ''optimize full maxtime unlimited'' )' ;
end sync;
/
---需要安装企业版 ,否则下面会报错 select * from v$version; SELECT * FROM V$OPTION WHERE PARAMETER = 'Online Index Build';
Set ServerOutput on
declare
v_job number;
begin
Dbms_Job.Submit
(
job => v_job,
what => 'sync;',
next_date => sysdate, /* default */
interval => 'sysdate + 1/720' /* = 1 day / ( 24 hrs * 30 min) = 2 mins */
);
Dbms_Job.Run ( v_job );
Dbms_Output.Put_Line ( 'Submitted as job # ' || to_char ( v_job ) );
end;
/