1前言
Oracle从7.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%';
有很多时候 使用instr和like是很理想的, 特别是搜索仅跨越很小的表的时候.然而通过这些文本定位的方法将导致全表扫描,对资源来说消耗比较昂贵,而且实现的搜索功能也非常有限.
利用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建立索引
2.1索引简介
利用Oracle Text对文档集合进行检索的时候,你必须先在你的文本列上建立索引。索引将文本打碎分成很多记号(token),这些记号通常是用空格分开的一个个单词。
Oracle Text应用的实现实际上就是一个 数据装载—> 索引数据—>执行检索 的一个过程。
2.1.1索引类型和限制
建立的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的介绍。
2.1.2权限和临时表空间
权限
用户不需要具有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在行row1、row2、row3都出现过,这样通过查找单词所对应的行的rowid就可以迅速找到文本记录。
在索引建好后,我们可以在该用户下查到Oracle自动产生了以下几个表:(假设索引名为myindex):DR$myindex$I、DR$myindex$K、DR$myindex$R、DR$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
DIRECT_DATASTORE
Data is stored internally in the text column. Each row is indexed as a single document.
MULTI_COLUMN_DATASTORE
Data is stored in a text table in more than one column. Columns are concatenated to create a virtual document, one per row.
DETAIL_DATASTORE
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.
FILE_DATASTORE
Data is stored externally in operating system files. Filenames are stored in the text column, one per row.
NESTED_DATASTORE
Data is stored in a nested table.
URL_DATASTORE
Data is stored externally in files located on an intranet or the Internet. Uniform Resource Locators (URLs) are stored in the text column.
USER_DATASTORE
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;
2.3.3 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_stoplists和ctx_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$I、DR$myindex$K、DR$myindex$R、DR$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服务进程,在后台的同步请求处理就会象事时一样,在你提交修改1,2秒后新的数据马上就被索引了。
与手工同步相比,自动索引同步更容易使索引变的稀疏,需要执行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 query。For 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 andpuppies 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 Reference Release 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;
7.Oracle 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用户视图中查看。
8.Oracle Text 支持检索对网页的文本检索
通过在表里面存储网络上各种格式的文本文件,HTML文件的路径Url。Oracle在建立索引的时候,可以顺着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的一些参数,其中timeout,proxy是经常用到的:
属性
属性值
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. 常见错误
下面就一些常见的错误信息给出解释和解决办法:
1.sync 失败
DRG-10595: ALTER INDEX T_DOC6_CT失败
DRG-50857: Oracle error in drsxsopen
ORA-01480: STR 赋值变量缺少空后缀
解决:这是8i的一个bug, 但可以避免它,方法是在同步之前先发一个语句:
ALTER SESSION SET nls_language=american;
2.create 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 号码)。
3.create 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不会因此而崩溃)。
7.create 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资源:
Oracle Text Application Developer's Guide Release 9.2
Oracle Text Reference Release 9.2
Oracle Text (formerly interMedia Text)
Oracle Technology Network (OTN) discuss forum
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.
"Managing DML Operations for a CONTEXT Index" in Chapter 2, "Indexing"
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.
"Index Optimization" in Chapter 2, "Indexing".
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
10.4示例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)