0
点赞
收藏
分享

微信扫一扫

【引用】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多毫秒

举报

相关推荐

0 条评论