Oracle的全文檢索

 

Oracle的全文檢索,以前用過,效果還可以,就是耗資源。

 

現在這裏也在考慮使用全文檢索,自己印象中有兩種:

1. Oracle的全文檢索(CTX),雖然其他數據庫也有全文檢索,不過這裏用的是Oracle,也就看看Oracle的全文檢索 --- 以前用的全文檢索也是Oracle,正好數據庫一樣;

 

2. Apache基金會的Lucene, 後續有企業級升級版應用 Solr

 Lucene以前也用過,Solr沒有用過;

以前用Lucene的時候,Solr還沒有出現,在多機使用同一套Lucene索引貌似還有一定講究,時間長了,不記得了。

 

這裏先抄一篇對Oracle全文檢索講的比較清晰的文章(以前用過,所以一看簡單說明,全部能記起來)

http://blog.itpub.net/10597729/viewspace-741700/

其實這篇文章也是轉載ITPUT論壇上的一篇文章

 

 

轉載自:http://www.iteye.com/topic/1118055

1.oracle text

首先這裏說oracle 全文檢索,是針對我機器上的oracle 10g版本的。至少 10g 或以上的版本 適合這樣來使用。

 

oracle 的全文檢索,操作步驟爲:將表中需要檢索的字段,創建爲全文檢索的索引,然後通過select * from T where contains(F,'test',1)>0的語句進行全文檢索,達到預期效果。

 

然後,oracle全文檢索還是很強大的,能夠檢索文本啊、還有其他多種格式的文檔。我做的測試只是針對數據庫中的某一個字段的檢索。比如針對地址表中的地址進行檢索。

 

2.準備操作

首先,先建一個表用於測試,在名爲testuser用戶下建表。

 

 

Sql代碼  
  1. create table YU_TEST(  
  2.     id number,  
  3.     name varchar2(50)  
  4. );  

 

插入測試數據

 

 

Sql代碼  
  1. insert into YU_TEST values(1,'重慶市沙坪壩區');  
  2. insert into YU_TEST values(2,'成都市青羊區');  
  3. insert into YU_TEST values(3,'北京市西城區');  
  4. insert into YU_TEST values(4,'重慶市兩江新區');  
  5. insert into YU_TEST values(5,'上海市浦東新區金橋鎮');  
  6. insert into YU_TEST values(6,'上海東方明珠');  
  7. insert into YU_TEST values(7,'江蘇省無錫市國家軟件園');  
  8. insert into YU_TEST values(8,'成都市天府軟件園');  
 

 

 

oracle全文檢索需要ctxsys用戶的支持,其實主要是需要使用ctxsys用戶下的ctx_ddl這個包,這個包中絕大部分過程的創建都與全文檢索有關。

 

首先需要對ctxsys用戶解鎖,以獲得ctx_ddl包的操作權。

進入system用戶,輸入如下命令,解鎖ctxsys用戶

 

 

Sql代碼  
  1. alter user ctxsys account unlock;  

 

然後將ctx_ddl包的操作權限賦給testuser用戶。

也是在system用戶下,輸入如下命令,賦予目標用戶ctx_ddl包操作權限

 

 

Sql代碼  
  1. grant execute on ctx_ddl to testuser;  

 

至此,準備工作已經完成了

 

3.創建分析器

oracle text的分析器,類似於lucene中的分詞器,將需要檢索的記錄,按照一定的方式進行詞組拆分,然後存放在索引表中。檢索的時候根據索引表中存放的拆分詞組,對傳入的關鍵字進行匹配,並返回匹配結果。

oracle text中的分析器有3種:

 

  • basic_lexer:只能根據空格和標點來進行拆分。比如“中國重慶”,只能拆分爲“中國重慶”一個詞組
  • chinese_vgram_lexer:專門的漢語分析器,按字單元進行拆分,比如“中國重慶”,可以拆分爲“中”、“中國“、”國重”、“重慶”、“慶”五個詞組。這種方式的好處是能夠將所有有可能的詞組全部保存進索引表,使得數據不會遺漏。
  • chinese_lexer:一種新的漢語分析器,能夠認識大部分常用的漢語詞彙,並按常用詞彙進行拆分存儲。比如“中國重慶”,只會被拆分爲“中國”、“重慶”兩個詞組。

 

這裏我使用chinese_lexer這個分詞器,用testuser用戶登錄,執行下面的命令,創建分析器。

 

 

Sql代碼  
  1. exec ctx_ddl.create_preference ('my_lexer''chinese_lexer');  

 

 

這句話的意思是,創建一個“chinese_lexer”分析器,名稱爲my_lexer。

 

4.創建過濾詞組

在我們建索引的時候,通常需要對一些常用的詞組進行過濾,比如對公司名稱進行檢索時,肯定不希望輸入“有限公司”、“公司”等關鍵詞時,也會有搜索結果。

用testuser用戶登錄,執行下面的命令,創建過濾詞組

 

 

Sql代碼  
  1. exec ctx_ddl.create_stoplist('my_stoplist');  

  

創建過濾詞組成功以後,需要自定義需要過濾的詞組

 

 

 

Sql代碼  
  1. ctx_ddl.add_stopword('my_stoplist','有限公司');  
  2. ctx_ddl.add_stopword('my_stoplist','股份有限公司');  

 

意思就是,創建了一個名爲“my_stoplist”的過濾詞組,“有限公司”、“股份有限公司”這兩個詞組不會被創建爲索引

 

5.創建索引

其實前面的工作,都是爲創建索引做準備的。

我要對YU_TEST表中的name字段進行檢索,首先必須對name字段創建索引。

這裏需要注意的是,name字段不能爲nvarchar2類型,並且這個表的主鍵也不能爲nvarchar2型,否則無法創建索引。

 

 

Sql代碼  
  1. create index YU_TEST_INDEX on YU_TEST(name) indextype is CTXSYS.CONTEXT parameters('lexer my_lexer stoplist my_stoplist');  
 

這句話的意思就是,在YU_TEST_INDEX表中的name字段上創建索引,索引類系那個爲context類型,該索引用到的分析器爲前面定義的my_lexer,該索引用到的過濾詞組爲前面定義得my_stoplist。

 

索引創建成功後,你回發現,在當前用戶的表中,多了四個表

 

 

其中YU_TEST表中name字段被拆分後的詞組保存在DR$YU_TEST_INDEX$I表中

 


這樣可以看見索引的詳細信息。

 

6.使用索引

 

 

Sql代碼  
  1. select * from YU_TEST where contains(name,'重慶')>0;  
 
  可以用contains來使用oracle的全文檢索。
這樣查出來的數據是沒有經過排序的,有時候我們的需求是按照關鍵字的匹配程度排序,使用下面的語句:
 
Sql代碼  
  1. select score(1),y.* from YU_TEST y where contains(name,'重慶',1)>0 order by score(1) desc;  
 
這裏的score是oracle全文檢索對關鍵字的匹配程度所計算的分數,contains裏的最後一個參數“1”就是對這個分數的一個標識
 
7.索引優化
前面已經能夠進行檢索了,現在就是對索引進行優化
當我們需要修改YU_TEST表中的數據,比如添加、刪除、更新等操作時,YU_TEST_INDEX索引是不會同步更新數據的,需要我們在程序中手動的更新,可以寫一個oracle的觸發器,當添加、刪除、修改操作時,進行索引更新。也可以定時進行更新。
索引同步:
 
Sql代碼  
  1. exec ctx_ddl.sync_index('yu_test_index')  
 
索引優化:
 
Sql代碼  
  1. exec ctx_ddl.optimize_index('yu_test_index','full')  
 
8.用戶輸入關鍵詞切詞
 
在執行檢索的時候會發現這樣的問題
 


 
表中存在兩條“重慶”相關的記錄和一條“天府”相關的記錄,但是我傳入“重慶天府”關鍵字時,確檢索不出記錄,這顯然不是我們想要的結果,因爲我們需要的是最大程度的匹配,傳入“重慶天府”時應該同時檢索出“重慶”相關和“天府”相關的信息。
要實現這種效果,需要用到oracle 10g的新特性,可以將傳入的關鍵詞先進行切詞,然後在進行檢索。
首先需要先創建一個POLICY過程
 
Sql代碼  
  1. exec CTX_DDL.CREATE_POLICY('MY_POLICY', LEXER => 'my_lexer');  
 
這裏創建了一個名稱爲my_policy的policy過程,分析器用到了前面創建的my_lexer分析器
 
寫一個oracle函數,來處理關鍵詞切詞:
Sql代碼  
  1. create or replace function p_split_chinese(p_input in varchar2)  
  2.     return varchar2 as   
  3.        v_tab CTX_DOC.TOKEN_TAB;  
  4.        v_return VARCHAR2(323767);  
  5.     begin  
  6.        CTX_DOC.POLICY_TOKENS('my_policy',p_input,v_tab);  
  7.        for i in 1..v_tab.count loop  
  8.           v_return := v_return || ',' || v_tab[i].token;  
  9.        end loop;  
  10.        return LTRIM(v_return,',');  
  11.      end;  
  12. /  
 
在plsql 中執行這個函數

這樣就可以將關鍵詞切詞,在程序中直接將這個切詞結果,進行檢索。
 

 
 可以看到這裏顯示的只有“天府”相關的信息,那麼“重慶”相關的呢?
先看一下DR$YU_TEST_INDEX$I表中的結構。


 


 

發現token_text字段中,只有重慶市、北京市這些詞,而沒有重慶、北京之類的
所以會發現,oracle全文檢索的chinese lexer分析器,對"重慶市",只會認爲這隻有一個詞組,而不會把“重慶市”分爲“重慶”和“重慶市”。
所以,如果需要比較精確的檢索到結果,還是chinese_vgram_lexer分析器最好用,能夠匹配到所有的詞組。

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章