介绍完Oracle全文索引的DATASTORE属性,继续介绍Oracle的FILTER属性。
Oracle全文索引的FILTER属性主要是针对具有一定格式的文档,Oracle根据FILTER的设定来过滤那些不属于文章内容的部分。FILTER属性包含的属性有:CHARSET_FILTER、INSO_FILTER、NULL_FILTER、USER_FILTER、PROCEDURE_FILTER几种。
这里仍然从FILTER的最简单属性NULL_FILTER开始介绍:
SQL> CREATE TABLE T (ID NUMBER, DOCS VARCHAR2(4000));
表已创建。
SQL> INSERT INTO T VALUES (1, 'This is a sample for Oracle TEXT.');
已创建 1 行。
SQL> INSERT INTO T VALUES (2, 'This is a null filter sample');
已创建 1 行。
SQL> COMMIT;
提交完成。
SQL> CREATE INDEX IND_T_DOCS ON T (DOCS) INDEXTYPE IS CTXSYS.CONTEXT
2 PARAMETERS ('FILTER CTXSYS.NULL_FILTER');
索引已创建。
SQL> SELECT * FROM T WHERE CONTAINS(DOCS, 'TEXT') > 0;
ID DOCS
---------- ----------------------------------------
1 This is a sample for Oracle TEXT.
上面就是一个简单的NULL_FILTER的例子,所谓NULL_FILTER就是不使用过滤。Oracle推荐对于文本、HTML和XML文件不使用过滤。
Oracle的FILTER的默认值和索引列的字段类型,以及DATASTORE的属性有关。如果是存储在数据库中的列,且列的类型为VARCHAR2、CHAR、CLOB,则Oracle会使用NULL_FILTER作为FILTER的默认属性。
其实上面这个例子和不指定FILTER是等价的。
SQL> DROP INDEX IND_T_DOCS;
索引已丢弃。
SQL> CREATE INDEX IND_T_DOCS ON T (DOCS) INDEXTYPE IS CTXSYS.CONTEXT;
索引已创建。
SQL> SELECT * FROM T WHERE CONTAINS(DOCS, 'FILTER') > 0;
ID DOCS
---------- ----------------------------------------
2 This is a null filter sample
这篇文章继续介绍全文索引的FILTER属性,介绍Oracle的INSO_FILTER属性。
Oracle的全文索引除了可以支持文本文件外,还可以支持多种文档格式,对于这些文档格式,在建立索引的时候需要指定INSO_FILTER参数,使用这个过滤参数,Oracle的全文索引可以识别绝大部分的常见文档,比如:word、pdf等。
下面看一个索引文档的例子:
SQL> conn myuser/myuser
Connected.
SQL> CREATE TABLE T (ID NUMBER, DOCS VARCHAR2(1000));
Table created.
SQL> INSERT INTO T VALUES (1, 'pdf.pdf');
1 row created.
SQL> INSERT INTO T VALUES (2, 'doc1.doc');
1 row created.
SQL> commit;
Commit complete.
SQL> begin
2 CTX_DDL.CREATE_PREFERENCE('TEST_FILE', 'FILE_DATASTORE');
3 CTX_DDL.SET_ATTRIBUTE('TEST_FILE', 'PATH', '/home/oracle');
4 END;
5 /
PL/SQL procedure successfully completed.
SQL> CREATE INDEX IND_T_DOCS ON T (DOCS) INDEXTYPE IS CTXSYS.CONTEXT
2 PARAMETERS ('DATASTORE TEST_FILE FILTER CTXSYS.INSO_FILTER');
Index created.
SQL> SELECT * FROM T WHERE CONTAINS(DOCS, 'ORACLE') > 0;
ID
----------
DOCS
--------------------------------------------------------------------------------
1
pdf.pdf
2
doc1.doc
SQL> SELECT * FROM T WHERE CONTAINS(DOCS, 'sina.com')>0;
ID
----------
DOCS
--------------------------------------------------------------------------------
1
pdf.pdf
下面试试如果修改文档的内容,是否还能查询得到?
先在doc1.doc中新入一行"ericlu"字符。
SQL> SELECT * FROM T WHERE CONTAINS(DOCS, 'ericlu')>0;
no rows selected
SQL> exec CTX_DDL.SYNC_INDEX('IND_T_DOCS');
PL/SQL procedure successfully completed.
SQL> SELECT * FROM T WHERE CONTAINS(DOCS, 'ericlu')>0;
no rows selected
同步过索引依然找不到
SQL> drop index IND_T_DOCS;
Index dropped.
SQL> CREATE INDEX IND_T_DOCS ON T (DOCS) INDEXTYPE IS CTXSYS.CONTEXT
2 PARAMETERS ('DATASTORE TEST_FILE FILTER CTXSYS.INSO_FILTER');
Index created.
SQL> SELECT * FROM T WHERE CONTAINS(DOCS, 'ericlu')>0;
ID
----------
DOCS
--------------------------------------------------------------------------------
2
doc1.doc
重建后可以找到,下面试了rebuild也可以的。
先在doc1.doc中加入"my cups"字符。
SQL> SELECT * FROM T WHERE CONTAINS(DOCS, 'my cups')>0;
no rows selected
SQL> alter index IND_T_DOCS rebuild;
Index altered.
SQL> SELECT * FROM T WHERE CONTAINS(DOCS, 'my cups')>0;
ID
----------
DOCS
--------------------------------------------------------------------------------
2
doc1.doc
FILTER的默认值并不是一成不变的,这个默认值会受索引字段类型和DATASTORE的类型的影响。在上一篇文章中
的测试中,对于存储在数据库中的VARCHAR2、CHAR和CLOB字段中的数据,Oracle自动选择了NULL_FILTEL。在这
里DATASTORE的属性为FILE_DATASTORE,则Oracle会默认选择 INSO_FILTER作为默认值。
SQL> DROP INDEX IND_T_DOCS;
Index dropped.
SQL> CREATE INDEX IND_T_DOCS ON T (DOCS) INDEXTYPE IS CTXSYS.CONTEXT
2 PARAMETERS ('DATASTORE TEST_FILE FILTER CTXSYS.INSO_FILTER');
Index created.
SQL> SELECT * FROM T WHERE CONTAINS(DOCS, 'ORACLE') > 0;
ID
----------
DOCS
--------------------------------------------------------------------------------
1
pdf.pdf
2
doc1.doc
SQL> DROP INDEX IND_T_DOCS;
Index dropped.
SQL> CREATE INDEX IND_T_DOCS ON T (DOCS) INDEXTYPE IS CTXSYS.CONTEXT
2 PARAMETERS ('DATASTORE TEST_FILE');
Index created.
SQL> SELECT * FROM T WHERE CONTAINS(DOCS, 'ORACLE') > 0;
ID
----------
DOCS
--------------------------------------------------------------------------------
1
pdf.pdf
2
doc1.doc
可以发现默当DATASTORE的属性为FILE_DATASTORE,Oracle会选择 INSO_FILTER作为默认值。
这篇文章继续介绍全文索引的FILTER属性,介绍对不同类型的数据采用不同类型的FILTER属性的方法。
如果Oracle索引的文档包括多种类型,比如doc文件、html文件、pdf文件、纯文本文件等等。由于Oracle不建议对HTML、XML和纯文本文件使用INSO_FILTER,因此没有一个统一适用的FILTER属性适合所有的文档。
Oracle提供了一种FORMAT列的方法,通过设置FORMAT列的内容可以通知Oracle建立何种FILTER甚至是不建立索引。
下面看一个简单的例子:
[oracle@rhel140 ~]$ sqlplus myuser/myuser
SQL*Plus: Release 10.2.0.1.0 - Production on Sun May 17 21:35:01 2009
Copyright (c) 1982, 2005, Oracle. All rights reserved.
Connected to:
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production
With the Partitioning, OLAP and Data Mining options
SQL> CREATE TABLE T (ID NUMBER, TYPE VARCHAR2(6), DOCS VARCHAR2(100));
Table created.
SQL> INSERT INTO T VALUES (1, 'BINARY', 'pdf.pdf');
1 row created.
SQL> INSERT INTO T VALUES (2, 'TEXT', 'OUTPUT.TXT');
1 row created.
SQL> INSERT INTO T VALUES (3, 'IGNORE', 'doc1.doc');
1 row created.
SQL> COMMIT;
Commit complete.
SQL> BEGIN
2 CTX_DDL.CREATE_PREFERENCE('TEST_FILE', 'FILE_DATASTORE');
3 CTX_DDL.SET_ATTRIBUTE('TEST_FILE', 'PATH', '/home/oracle');
4 END;
5 /
PL/SQL procedure successfully completed.
SQL> CREATE INDEX IND_T_DOCS ON T (DOCS) INDEXTYPE IS CTXSYS.CONTEXT
2 PARAMETERS ('DATASTORE TEST_FILE FILTER CTXSYS.INSO_FILTER FORMAT COLUMN TYPE');
Index created.
SQL> SELECT * FROM T WHERE CONTAINS(DOCS, 'ORACLE') > 0;
ID TYPE
---------- ------
DOCS
--------------------------------------------------------------------------------
2 TEXT
OUTPUT.TXT
1 BINARY
pdf.pdf
'doc1.doc'文档中有'ORACLE'字符串没有被查到,是因为'IGNORE'起了作用。
发现表T的列TYPE的值很重要,是因为FORMAT COLUMN指定为“TYPE”列,设置为BINARY的文档使用INST_FILTER,设置为TEXT的文档使用NULL_FILTER,设置为IGNORE的文档不进行索引。
SQL> update t set type='BINARY' where id=3;
1 row updated.
SQL> commit;
Commit complete.
SQL> exec ctx_ddl.sync_index('IND_T_DOCS');
PL/SQL procedure successfully completed.
SQL> SELECT * FROM T WHERE CONTAINS(DOCS, 'ORACLE') > 0;
ID TYPE
---------- ------
DOCS
--------------------------------------------------------------------------------
2 TEXT
OUTPUT.TXT
1 BINARY
pdf.pdf
sync过还是查询不到,需要做rebuild
SQL> alter index IND_T_DOCS rebuild;
Index altered.
SQL> SELECT * FROM T WHERE CONTAINS(DOCS, 'ORACLE') > 0;
ID TYPE
---------- ------
DOCS
--------------------------------------------------------------------------------
1 BINARY
pdf.pdf
2 TEXT
OUTPUT.TXT
3 BINARY
doc1.doc
这篇文章继续介绍全文索引的FILTER属性,介绍全文索引的PROCEDURE_FILTER属性。
对于一些复杂的需求,Oracle 自定义的FILTER可能并不合适,Oracle支持用户自己编写用于FILTER的程序。Oracle提供两种方式,一种是USER_FILTER一种 是PROCEDURE_FILTER。USER_FILTER是用户编译好的命令行程序,而PROCEDURE_FILTER是调用用户编写的存储过程。
下面举一个PROCEDURE_FILTER的例子,在例子中被索引的文档为纯文本格式,要求是不索引文档中注释部分的内容。这种要求是预定义FILTER无法达到的,只能通过PROCEDURE_FILTER的方式来解决,下面看这个例子:
SQL> CREATE TABLE T (ID NUMBER, DOCS VARCHAR2(4000));
表已创建。
SQL> INSERT INTO T VALUES (1, 'This is a example for procedure filter.
2 In the this example, the words in comments are not indexed.');
已创建 1 行。
SQL> INSERT INTO T VALUES (2, '--This line should not be indexed.
2 /* And this two lines
3 should not be indexed too. */
4 Only this line is the real context.');
已创建 1 行。
SQL> COMMIT;
提交完成。
SQL> CREATE INDEX IND_T_DOCS ON T(DOCS) INDEXTYPE IS CTXSYS.CONTEXT;
索引已创建。
SQL> SELECT ID FROM T WHERE CONTAINS(DOCS, 'INDEXED') > 0;
ID
----------
2
1
对于普通的索引,是不会区分注释内容和正文内容的。
SQL> DROP INDEX IND_T_DOCS;
索引已丢弃。
SQL> CREATE OR REPLACE FUNCTION F_COMMENTS (P_STR IN VARCHAR2, P_FLAG IN NUMBER) RETURN VARCHAR2 AS
2 V_POSITION1 NUMBER;
3 V_POSITION2 NUMBER;
4 BEGIN
5 IF P_FLAG = 0 THEN
6 V_POSITION1 := INSTR(P_STR, '/*');
7 V_POSITION2 := INSTR(P_STR, '--');
8 IF V_POSITION1 = 0 THEN
9 V_POSITION1 := 40000;
10 END IF;
11 IF V_POSITION2 = 0 THEN
12 V_POSITION2 := 40000;
13 END IF;
14 IF V_POSITION1 < V_POSITION2 THEN
15 RETURN SUBSTR(P_STR, 1, V_POSITION1 - 1) || F_COMMENTS(SUBSTR(P_STR, V_POSITION1 + 2), 1);
16 ELSIF V_POSITION2 < V_POSITION1 THEN
17 RETURN SUBSTR(P_STR, 1, V_POSITION2 - 1) || F_COMMENTS(SUBSTR(P_STR, V_POSITION2 + 2), 2);
18 ELSE
19 RETURN P_STR;
20 END IF;
21 ELSIF P_FLAG = 1 THEN
22 RETURN F_COMMENTS(SUBSTR(P_STR, INSTR(P_STR, '*/') + 2), 0);
23 ELSIF P_FLAG = 2 THEN
24 V_POSITION2 := INSTR(P_STR, CHR(10));
25 IF V_POSITION2 != 0 THEN
26 RETURN F_COMMENTS(SUBSTR(P_STR, V_POSITION2 + 1), 0);
27 ELSE
28 RETURN NULL;
29 END IF;
30 END IF;
31 END;
32 /
函数已创建。
SQL> CREATE OR REPLACE PROCEDURE P_MYFILTER (P_INSTR IN VARCHAR2, P_OUTSTR IN OUT VARCHAR2) AS
2 BEGIN
3 P_OUTSTR := F_COMMENTS(P_INSTR, 0);
4 END;
5 /
过程已创建。
SQL> CONN CTXSYS/CTXSYS@YANGTK
已连接。
SQL> CREATE OR REPLACE PROCEDURE P_TESTFILTER(P_INSTR IN VARCHAR2, P_OUTSTR IN OUT VARCHAR2) AS
2 BEGIN
3 YANGTK.P_MYFILTER(P_INSTR, P_OUTSTR);
4 END;
5 /
过程已创建。
SQL> GRANT EXECUTE ON P_TESTFILTER TO YANGTK;
授权成功。
SQL> BEGIN
2 CTX_DDL.CREATE_PREFERENCE('TEST_PROCEDURE_FILTER', 'PROCEDURE_FILTER');
3 CTX_DDL.SET_ATTRIBUTE('TEST_PROCEDURE_FILTER', 'PROCEDURE', 'P_TESTFILTER');
4 CTX_DDL.SET_ATTRIBUTE('TEST_PROCEDURE_FILTER', 'INPUT_TYPE', 'VARCHAR2');
5 CTX_DDL.SET_ATTRIBUTE('TEST_PROCEDURE_FILTER', 'OUTPUT_TYPE', 'VARCHAR2');
6 END;
7 /
PL/SQL 过程已成功完成。
SQL> CONN YANGTK/YANGTK@YANGTK
已连接。
SQL> CREATE INDEX IND_T_DOCS ON T(DOCS) INDEXTYPE IS CTXSYS.CONTEXT
2 PARAMETERS ('FILTER CTXSYS.TEST_PROCEDURE_FILTER');
索引已创建。
SQL> SELECT ID FROM T WHERE CONTAINS(DOCS, 'INDEXED') > 0;
ID
----------
1
PROCEDURE_FILTER属性的设置方法和USER_DATASTORE属性的设置方法十分类似,都是必须使用CTXSYS用户来调用用户编译的过程。且CTXSYS用户封装的过程还必须授权给建立索引的用户。
使用自定义的过程来进行过滤,将文档内容中的注释内容过滤掉,索引查询的内容已经不包含注释的内容了。
对于如何判断注释内容,可以参考:http://yangtingkun.itpub.net/post/468/184024