【引用】Oracle全文检索方面的研究(全10)

4、操作实例

4.1 单列与多列支持中文检索

Create table mytable1(id number primary key, doc1 varchar2(400),doc2 clob,doc3 clob);

 

Insert into mytable1 values(1,'今天的天气很不错,我想去逛街','今天是星期天,不用上班。天天好心情','明天是星期一,要上班。心情不好');

Insert into mytable1 values(2,'天是蓝色的,万里无云。天气非常好。','天是多云的,天气看起来要下雨了。不适宜出门','天正在下雨,大雨倾盆。不能出门。');

Insert into mytable1 values(3,'this is a text','this is a word','this is a pdf');

Commit;

 

--先删除引用

begin

ctx_ddl.drop_preference('my_chinese_vgram_lexer');

ctx_ddl.drop_preference('my_chinese_lexer');

end;

 

--支持中文分词

Begin

ctx_ddl.create_preference('my_chinese_vgram_lexer', 'chinese_vgram_lexer');

ctx_ddl.create_preference('my_chinese_lexer', 'chinese_lexer');

End;

 

--先删除引用

begin

ctx_ddl.drop_preference('my_multi');

end;

--多列查询,如果仅仅是单列,则不用设置这个类型

Begin

Ctx_ddl.create_preference('my_multi', 'multi_column_datastore');

Ctx_ddl.set_attribute('my_multi', 'columns', 'doc1, doc2, doc3');

End;

 

 

drop index myindex;

 

--单列查询,支持中文的索引建立

Create index myindex on mytable(docs)

indextype is ctxsys.context

parameters ('datastore ctxsys.default_datastore lexer foo.my_chinese_lexer')

 

drop index idx_mytable;

--多列查询,支持中文的索引的建立

Create index idx_mytable on mytable1(doc1)indextype is ctxsys.context

parameters('datastore my_multi lexer foo.my_chinese_lexer');

 

--chinese_lexer词法分析器下的结果,三列都可以查询

Select * from mytable1 where contains(doc1, '今天')>0;  --检索到第一条数据

Select * from mytable1 where contains(doc1, '不适宜')>0; --检索到第二条数据

Select * from mytable1 where contains(doc1, '适宜')>0; --检索不到数据,他的分词技术太简单,将‘不适宜’作为一个词了

Select * from mytable1 where contains(doc1, '出门')>0;  --检索到第二条数据

Select * from mytable1 where contains(doc1, 'this is a word')>0;  --检索到第三条数据,中英文适用

 

 

--chinese_vgram_lexer词法分析器下的结果,

--chinese_vgram_lexer词法分析器虽然没那么智能,但检索结果往往比较符合我们的要求,

--如:“不适宜”这个词语应该拆分为“不适宜”和“适宜”两个词语,而不是单独的作为一个词语,

--chinese_vgram_lexer可以查询的到,而chinese_lexer不可以。

drop index idx_mytable;

--多列查询,支持中文的索引的建立

Create index idx_mytable on mytable1(doc1)indextype is ctxsys.context

parameters('datastore my_multi lexer foo.my_chinese_vgram_lexer');

 

--chinese_vgram_lexer词法分析器下的结果,三列都可以查询

Select * from mytable1 where contains(doc1, '今天')>0;  --检索到第一条数据

Select * from mytable1 where contains(doc1, '不适宜')>0; --检索到第二条数据

Select * from mytable1 where contains(doc1, '适宜')>0; --检索到第二条数据,这个分词虽然效率低点,但检索结果还可以

Select * from mytable1 where contains(doc1, '出门')>0;  --检索到第二条数据

Select * from mytable1 where contains(doc1, 'this is a word')>0;  --检索到第三条数据,中英文适用

 

 

--对于多列查询,更新列操作

--只更新从表,看是否能查到更新的信息

Update mytable1 set doc2='adladlhadad this datastore when your text is stored test' where id=2;

 

--同步更新索引

Begin

Ctx_ddl.sync_index('idx_mytable');

End;

--可见,虽然你检索是三个列,但是你更新的不是索引对应的那列(doc1),同步了索引也是不起作用的

Select * from mytable1 where contains(doc1,'adladlhadad')>0; --没有记录

 

--更新与doc1列原来相同内容(实际内容不变,只有操作而已)

Update mytable1 set doc1='天是蓝色的,万里无云。天气非常好。' where id=2;

 

--再同步更新索引

Begin

Ctx_ddl.sync_index('idx_mytable');

End;

 

--再查询一次

Select * from mytable1 where contains(doc1,'adladlhadad')>0; --有结果,可见,对于其他查询的列(非索引对应的列)的更新操作,可以连同索引对应的列一起更新,只是不改变索引的内容即可做到同步索引就可以出现效果了。

4.2 本地磁盘检索

create table mytable3(id number primary key, docs varchar2(2000));

 

insert into mytable3 values(111555,'1.txt');

 

insert into mytable3 values(111556,'1.doc');

 

insert into mytable3 values(111557,'1.xls');

 

insert into mytable3 values(111558,'1.pdf');

 

insert into mytable3 values(111559,'2.txt');

 

insert into mytable3 values(111560,'2.doc');

 

insert into mytable3 values(111561,'2.xls');

 

insert into mytable3 values(111562,'2.pdf');

 

commit;

 

 

--先删除引用

begin

ctx_ddl.drop_preference('COMMON_DIR');

end;

 

--建立 file datastore

begin

ctx_ddl.create_preference('COMMON_DIR','FILE_DATASTORE');

ctx_ddl.set_attribute('COMMON_DIR','PATH','D:\search');

end;

 

--先删除索引

drop index myindex3;

--建立索引,8个文件,内容简单,耗时1.5s

create index myindex3 on mytable3(docs) indextype is ctxsys.context parameters ('datastore COMMON_DIR lexer foo.my_chinese_lexer');

 

select * from mytable3 where contains(docs,'text')>0; --查询,支持txt

select * from mytable3 where contains(docs,'pdf')>0; --查询,支持pdf

select * from mytable3 where contains(docs,'excel')>0; --查询,支持excel

select * from mytable3 where contains(docs,'word')>0; --查询,支持doc

select * from mytable3 where contains(docs,'文本')>0; --查询,支持中文

select * from mytable3 where contains(docs,'文档')>0; --查询,支持中文

select * from mytable3 where contains(docs,'阅读')>0; --查询,支持中文pdf

select * from mytable3 where contains(docs,'这是Excel')>0; --查询,支持中文

 

--暂时测试支持doc,txt,xls,pdf

 

--更新了文件内容2.txt

select * from mytable3 where contains(docs,'这个测试用的文本')>0; --查询无更新好数据

--不同步索引,无效

--同步更新索引

Begin

Ctx_ddl.sync_index('myindex3');

End;

--再次查询

select * from mytable3 where contains(docs,'测试')>0; --还是无效

 

--用相同的值取代2.txt然后再同步索引

Update mytable3 set docs='2.txt' where id=111559;

 

--再同步索引

--同步更新索引

Begin

Ctx_ddl.sync_index('myindex3');

End;

--再次查询

select * from mytable3 where contains(docs,'测试')>0; --结果出现,可见,单更新文件内容,同步索引是无效的,索引认的是数据库纪录,数据库纪录改变,索引才会更新

 

--新增加文件,结果雷同。关键是要更新数据库纪录,即使改了文件内容,也要用相同的值update数据库纪录一次。

4.3 检索结果高亮显示

Create table my_high (id number primary key, docs varchar2(1000));

insert into my_high values (1, 'this is a oracle text example. And oracle is the key word.');

insert into my_high values (2, '<title>oracle text</title><body>this is a oracle ctx_doc hightlight example.</body>');

commit;

/

--建立索引

create index ind_m_high on my_high(docs) indextype is ctxsys.context;

--返回结果的偏移量

set serverout on

declare

v_restab ctx_doc.highlight_tab;

begin

ctx_doc.highlight('ind_m_high', 1, 'oracle', v_restab, true);

for i in 1..v_restab.count loop

dbms_output.put_line('begin with: ' || v_restab(i).offset || ' length: ' || v_restab(i).length);

end loop;

end;

/

begin with: 11 length: 6

begin with: 36 length: 6

 

ctx_doc.highlight参数说明:

ctx_doc.highlight(索引,数据库中的ID, 搜索关键字, 指明返回的偏移量是针对纯文本格式还是针对HTML格式, true);

true or false: 对比PLAINTEXT设置为FALSE和TRUE的区别可以发现,对于HTML所有的标识部分,Oracle统一认为长度等于2。

对于True: <title>oracle text</title><body>this is a oracle ctx_doc hightlight example.</body>,<title>认为是2个长度,false的话就全部纪录,认为总共有7个字符长度。

 

                要在sqlplus执行

 

 

4.4 具体测试

               

 

 

4.4.1 基本的全文检索

--先删除引用

begin

ctx_ddl.drop_preference('my_chinese_vgram_lexer');

ctx_ddl.drop_preference('my_chinese_lexer');

end;

 

--支持中文分词

Begin

ctx_ddl.create_preference('my_chinese_vgram_lexer', 'chinese_vgram_lexer');

ctx_ddl.create_preference('my_chinese_lexer', 'chinese_lexer');

End;

Begin

Ctx_ddl.create_preference('F_DOCNEWS_Preference', 'multi_column_datastore');

Ctx_ddl.set_attribute('F_DOCNEWS_Preference', 'columns', 'F_CONTENT,F_DESCRIPTION,F_TITLE');

End;

drop index f_content_index;

Create index f_content_index on T_DOCNEWS(F_CONTENT)

indextype is ctxsys.context

parameters('datastore F_DOCNEWS_Preference lexer foo.my_chinese_lexer');

Select * from T_DOCNEWS where contains(F_CONTENT,'菲律宾')>0; --有结果,

 

 

4.4.2 带动态摘要的高亮全文检索

--先删除引用

begin

ctx_ddl.drop_preference('my_chinese_vgram_lexer');

ctx_ddl.drop_preference('my_chinese_lexer');

end;

 

--支持中文分词

Begin

ctx_ddl.create_preference('my_chinese_vgram_lexer', 'chinese_vgram_lexer');

ctx_ddl.create_preference('my_chinese_lexer', 'chinese_lexer');

End;

 

--先删除索引

drop index f_content_index;

 

--新建索引,默认属性,无过滤器,支持中文高级分词

Create index f_content_index on T_DOCNEWS(F_CONTENT)

indextype is ctxsys.context

parameters('datastore ctxsys.default_datastore filter ctxsys.null_filter section group

ctxsys.html_section_group lexer foo.my_chinese_lexer');

 

 

 

 

?--以下开始准备建立存储过程,先定义数组类型

CREATE or replace TYPE f_content_arr AS OBJECT(

id NUMBER ,

url varchar2(255),

title varchar2(255),

abstractcontent varchar2(255)

);

 

--定义数组变量

CREATE or replace type f_content_arr_re as table of f_content_arr;

 

--定义存储过程

create or replace procedure f_content_pro (keyword in varchar,v_cfjg out f_content_arr_re) is

v_restab ctx_doc.highlight_tab;

begin

DECLARE

i number;

s clob;

startnum number;

endnum number;

v_res_fun T_DOCNEWS%rowTYPE;

cursor c_fun is

select *  from T_DOCNEWS where contains(F_CONTENT,keyword)>0;

BEGIN

i := 0;

v_cfjg := f_content_arr_re();

open c_fun;

LOOP

fetch c_fun

into v_res_fun;

EXIT WHEN c_fun%NOTFOUND;

i := i + 1;

s := v_res_fun.F_CONTENT;

v_cfjg.EXTEND;

ctx_doc.highlight('f_content_index', v_res_fun.F_ID, keyword, v_restab, false);

--只取第一个,没有loop循环

startnum:=v_restab(1).offset;

if v_restab(1).offset > 30 then

   begin

        startnum := v_restab(1).offset-30 ;

   end;

end if;

if v_restab(1).offset <= 30 then

   begin

        startnum := 1 ;

   end;

end if;

if length(s)-v_restab(1).offset > 30 then

   begin

        endnum := v_restab(1).offset+30 ;

   end;

end if;

if length(s)-v_restab(1).offset <= 30 then

   begin

        endnum := length(s) ;

   end;

end if;

v_cfjg(v_cfjg.count) := f_content_arr(v_res_fun.F_ID,v_res_fun.F_URL,v_res_fun.F_TITLE,substr(s,startnum,endnum-startnum));

dbms_output.new_line();

END LOOP;

end;

EXCEPTION

WHEN TOO_MANY_ROWS THEN

DBMS_OUTPUT.PUT_LINE('TOO_MANY_ROWS');

WHEN OTHERS THEN

DBMS_OUTPUT.PUT_LINE(sqlerrm);

end f_content_pro;

 

--在此,全文检索存储过程定义完毕

 

 

 

--以下是sqlplus调用

 

declare

   s f_content_arr_re;

 begin   

   f_content_pro('菲律宾',s);    

 END; 

 

 

       Java后台调用存储过程并返回参数代码:

            public ArrayList<DocNews> search(String keyword) {

                  ArrayList<DocNews> list = new ArrayList<DocNews>();

                  Connection conn = null;

                  ResultSet rs = null;

                  CallableStatement stmt = null;

                  DocNews docnews;

                  try {

                        conn = DBPool.getConnection();

                        stmt = null;

               String procName = new StringBuffer().append("{ call f_content_pro(?,?) } ").toString();

                        stmt = conn.prepareCall(procName);

                        stmt.setString(1, keyword);

                        stmt.registerOutParameter(2, Types.ARRAY, "F_CONTENT_ARR_RE");

                        stmt.execute();

                        ARRAY arr = (ARRAY) stmt.getArray(2);

                        rs = arr.getResultSet();

                        while (rs.next()) {

                              STRUCT struct = (STRUCT) rs.getObject(2);

                              Object[] obs = struct.getAttributes();

                              docnews = new DocNews();

                              docnews.setId(((BigDecimal)obs[0]).longValue());

                              docnews.setUrl((String)obs[1]);

                              docnews.setTitle((String)obs[2]);

                              docnews.setAbstractcontent((String)obs[3]);

                              list.add(docnews);

                        }

                        if (stmt != null) {

                              stmt.close();

                        }

                        if (conn != null) {

                              conn.close();

                        }

                  } catch (Exception e) {

                        e.printStackTrace();

                  }

                  return list;

         }

 

       注:在java中调用方法,除了在项目里加入class12.jar包以外,还需要加入Oracle自带的orai18n.jar包,如果仅仅是执行main方面,则可以,但如果是web项目,则要将orai18n.jar包加入到jdk的%jdk%\jre\lib\ext目录中才行。如果没有orai18n.jar这个包会造成检索调用存储过程返回结果是乱码(???三个问号)。

 

4.4.3 检索简单界面图

 

 

 

5.检索性能

                        执行以下索引

Create index f_content_index on T_DOCNEWS(F_CONTENT)

indextype is ctxsys.context

parameters('datastore F_DOCNEWS_Preference lexer foo.my_chinese_lexer');

总共5272条新闻,总耗时61s

合计约一分钟5000条

 

查询仅需200多毫秒

你可能感兴趣的:(oracle,F#,Excel,ext,全文检索)