首頁 > IT教程 > 正文

全文檢索中近義詞、關聯詞的解決方案

2020-02-13 10:31:02 來源:金橙教程網 作者:admin8 瀏覽:67次 「手機版」

解決的近義詞

一直想找到一個好的同義詞解決方案,在百度和google查找,大家對于這個問題都只是寥寥數語,不愿講清,我在javaeye搜此類信息也求不到,后來發了個提問貼也只有瀏覽數而無回復,不知道這是什么原因,無奈之下我只有自己研究。

因為沒有其它的解決方案可以借鑒,以下純為我個人的見解。

我認為所謂近義詞、關聯詞檢索不外乎以下三種形式:

1.類似google suggest,用戶輸入關鍵字后自動提示功能。

2.假如“奧運會”的關聯詞是“北京”,用戶輸入“奧運會”搜索時,將“奧運會”的搜索結果以及“北京”的搜索結果都搜出來。

3.用戶輸入“奧運會”搜索,只顯示“奧運會”的結果,它的關聯詞在結果集的底端用“相關搜索”的形式提示給用戶。

?

?

這三種方式中看似2和3是類似的,其實還是有些很大區別的,我個人認為在實際的應用中,第3種方式是最多見的。

先說說第1種方式

google suggest的在網上可以搜到完整的解決方案,跟我此前想的一樣。

建一個用來存儲關鍵字的表

例如:??

Sql代碼 復制代碼

  1. create?table??KEYS(???? ??
  2. ???ITEM_ID?varcha2(50)?not?null,???? ??
  3. ???SEARCH_KEY?varchar2(100),???????? ??
  4. );????

Sql代碼 復制代碼

  1. create?table??KEYS(???? ??
  2. ???ITEM_ID?varcha2(50)?not?null,???? ??
  3. ???SEARCH_KEY?varchar2(100),???????? ??
  4. );????

create table  KEYS(    
   ITEM_ID varcha2(50) not null,    
   SEARCH_KEY varchar2(100),        
);  

SEARCH_KEY里存儲一些檢索的關鍵字,如“奧運”,“奧運會”,“北京奧運會”等.

當用戶在文本框敲入時,使用AJAX將當前文本框的內容發到Action,進行下面類似的操作

Java代碼 復制代碼

  1. String?key?=?request.getParameter("inputValue"); ??
  2. String?sql?=?"select?*?from?KEYS?as?k?where?k.SEARCH_KEY?like?'%key%'";??

Java代碼 復制代碼

  1. String?key?=?request.getParameter("inputValue"); ??
  2. String?sql?=?"select?*?from?KEYS?as?k?where?k.SEARCH_KEY?like?'%key%'";??

String key = request.getParameter("inputValue");
String sql = "select * from KEYS as k where k.SEARCH_KEY like '%key%'";

這個辦法也就是把當前文本框中的字放到數據庫中做模糊查詢,比如用戶輸入“奧”這個字,會從搜出所有包含“奧”字的詞,然后將這些詞包裝好,發回至頁面,頁面上用JS畫出下拉框,將這些包含“奧”字的詞填入就可以了。

google做的是右邊匹配,即是用的 like?'key%' ,輸入“奧”字,會出現所有以“奧”字開頭的詞的提示。

大家應該注意到google suggest的提示詞的最后面都寫有“約XXXXX條記錄”,我現在唯一能想到的解決方案是,當我們在Action中拿到匹配的詞之后,將匹配的詞搜索一次即可得出具體記錄的條數,其實這樣也不耗多少資源。

第2種方式

將關鍵詞和關鍵詞所聯的詞的結果都搜出來

我個人認為這個功能在實際的應用中只需用到其5%就可以了,因為在用戶體驗方面考慮,我們必須保障搜索結果的質量。關鍵詞的近義詞可以在結果頁的底端提示給用戶,也就是我上邊說到的第3種方式。

我所說的只需用到5%的意思是只需為少數的、必須轉義的詞實現這樣的功能

比如搜索“China”,那么“中國”這個詞的結果肯定得出現在結果集里,還有比如搜索“08”,那么“2008”的結果也得出現在結果集里,這些都是一些特定情況,其它的詞沒必要做成這樣,只需做提示即可。

現在中文檢索一般都是基于詞庫的檢索,實現第2種方式這個功能必須將之前的詞庫以及分詞算法進行修改。

一般的方法是在詞庫中把同義詞寫成一行,如將“中國 China”寫在同一行,然后修改Token算法(這個大家可以去研究一下,我現在使用的Analyzer包是公司商業上的伙伴提供的測試包,這個測試包已經可以滿足很大一部分需求了,我還不知道能不能共享)

其實原理是在索引時將原詞和原詞的近義詞一起索引

具體給大家個例子

有這樣一句話? ---- “中國是世界上人口最多的國家”

如果沒有為其做同義詞,大家可能會索引成這樣

[中國][世界][人口][最多][國家]

如果做了同義詞,會索引成

[中國][China][世界][人口][最多][國家]

即是說在索引時就已經將“中國”這個詞存成[中國][China]了,搜索時無論搜“中國”或是“China”都可以搜到這句話。

其實這個功能相當大一部分是依賴第三方的jar包,說來說去也沒多大意思,并且大多數情況下我們需要的并不同這種功能,而是更人性化的查詢提示的功能,也就是第3種方式。

第3種方式

這種方式是我在原有的系統上改進完成的

原有的系統是 compass + paoding + lucene

由于我不太熟compass的搜索,所以我還是采用的lucene搜索。相信大家對這種搭配的全文檢索已經非常熟悉了,paoding的詞庫是可以自己配置的。

那么怎樣在原有的基礎實現關鍵詞提示功能呢???

我的做法是這樣的,我按照paoding詞庫的特點新建了一個mydictionary.dic文件放在classpath下,里面的內容大致如下。

第1行:奧運會? 北京? 2008? 第29屆奧運會

第2行:中國 China ××× 中國電信 ×××

........

然后在服務器啟動時,將mydictionary.dic這個文件中的文字一行行讀入,分別做索引

Java代碼 復制代碼

  1. ????InputStream?fi?=?this.getClass().getClassLoader().getResourceAsStream("mydictionary.dic");?????? ??
  2. ????File?indexDir?=?new?File("d:\\tong"); ??
  3. ????Analyzer?luceneAnalyzer?=?new?StandardAnalyzer();???? ??
  4. ????????IndexWriter?indexWriter?=?new?IndexWriter(indexDir,?luceneAnalyzer,true);??????? ??
  5. BufferedReader?reader?=?new?BufferedReader(new?InputStreamReader(fi,?"UTF-8"));??? ??
  6. String?line?=?new?String();???????? ??
  7. while?((line?=?reader.readLine())?!=?null)? ??
  8. {??? ??
  9. ????Document?document?=?new?Document(); ??
  10. ????Field?FieldName?=?new?Field("line",?line,Field.Store.YES,?Field.Index.TOKENIZED); ??
  11. ????document.add(FieldName); ??
  12. ????indexWriter.addDocument(document); ??
  13. }??? ??
  14. indexWriter.optimize(); ??
  15. ????????indexWriter.close(); ??
  16. reader.close();??????

Java代碼 復制代碼

  1. ????InputStream?fi?=?this.getClass().getClassLoader().getResourceAsStream("mydictionary.dic");?????? ??
  2. ????File?indexDir?=?new?File("d:\\tong"); ??
  3. ????Analyzer?luceneAnalyzer?=?new?StandardAnalyzer();???? ??
  4. ????????IndexWriter?indexWriter?=?new?IndexWriter(indexDir,?luceneAnalyzer,true);??????? ??
  5. BufferedReader?reader?=?new?BufferedReader(new?InputStreamReader(fi,?"UTF-8"));??? ??
  6. String?line?=?new?String();???????? ??
  7. while?((line?=?reader.readLine())?!=?null)? ??
  8. {??? ??
  9. ????Document?document?=?new?Document(); ??
  10. ????Field?FieldName?=?new?Field("line",?line,Field.Store.YES,?Field.Index.TOKENIZED); ??
  11. ????document.add(FieldName); ??
  12. ????indexWriter.addDocument(document); ??
  13. }??? ??
  14. indexWriter.optimize(); ??
  15. ????????indexWriter.close(); ??
  16. reader.close();??????

    	InputStream fi = this.getClass().getClassLoader().getResourceAsStream("mydictionary.dic");    	
    	File indexDir = new File("d:\\tong");
    	Analyzer luceneAnalyzer = new StandardAnalyzer();    
        	IndexWriter indexWriter = new IndexWriter(indexDir, luceneAnalyzer,true);    	
	BufferedReader reader = new BufferedReader(new InputStreamReader(fi, "UTF-8"));   
	String line = new String();    	   
	while ((line = reader.readLine()) != null) 
	{   
		Document document = new Document();
		Field FieldName = new Field("line", line,Field.Store.YES, Field.Index.TOKENIZED);
		document.add(FieldName);
		indexWriter.addDocument(document);
	}   
	indexWriter.optimize();
        	indexWriter.close();
	reader.close();    

在搜索時,對用戶輸入的關鍵字正常搜索出結果后,對關鍵字進行第二次搜索,搜的是同義詞的索引,得到的結果按空格分開,即可得到所有同義詞,然后將同義詞發送至頁面即可。

Java代碼 復制代碼

  1. public?List?getTongYi(String?searchword)?throws?Exception ??
  2. { ??
  3. ????List?list?=?new?ArrayList(); ??
  4. ????Hits?hits?=?null;???? ??
  5. ????String?queryString?=?searchword;???? ??
  6. ????Query?query?=?null;???? ??
  7. ????String?result?=?""; ??
  8. ????IndexSearcher?searcher?=?new?IndexSearcher("d:\\tong");???????????? ??
  9. ????Analyzer?analyzer?=?new?StandardAnalyzer();???? ??
  10. ????QueryParser?qp?=?new?QueryParser("line",?analyzer);???? ??
  11. ????query?=?qp.parse(queryString);?????????????? ??
  12. ????if?(searcher?!=?null) ??
  13. ????{???? ??
  14. ????????hits?=?searcher.search(query);?????? ??
  15. ????????for(int?i=0;i<hits.length();i++) ??
  16. ????????{ ??
  17. ????????????Document?doc?=?hits.doc(i); ??
  18. ????????????System.out.println((i+1)+"."+doc.get("line")); ??
  19. ????????????result?=?doc.get("line"); ??
  20. ????????}?????????? ??
  21. ????} ??
  22. ????if(result!=null?&&?!result.equals("")) ??
  23. ????{ ??
  24. ????????String?[]?manyresult?=?result.split("?"); ??
  25. ????????for(int?i=0;i<manyresult.length;i++) ??
  26. ????????{ ??
  27. ????????????if(manyresult[i]!=null?&&?!manyresult[i].trim().equals("")) ??
  28. ????????????{ ??
  29. ????????????????list.add(manyresult[i]); ??
  30. ????????????} ??
  31. ????????} ??
  32. ????} ??
  33. ????return?list;???? ??
  34. }?????

Java代碼 復制代碼

  1. public?List?getTongYi(String?searchword)?throws?Exception ??
  2. { ??
  3. ????List?list?=?new?ArrayList(); ??
  4. ????Hits?hits?=?null;???? ??
  5. ????String?queryString?=?searchword;???? ??
  6. ????Query?query?=?null;???? ??
  7. ????String?result?=?""; ??
  8. ????IndexSearcher?searcher?=?new?IndexSearcher("d:\\tong");???????????? ??
  9. ????Analyzer?analyzer?=?new?StandardAnalyzer();???? ??
  10. ????QueryParser?qp?=?new?QueryParser("line",?analyzer);???? ??
  11. ????query?=?qp.parse(queryString);?????????????? ??
  12. ????if?(searcher?!=?null) ??
  13. ????{???? ??
  14. ????????hits?=?searcher.search(query);?????? ??
  15. ????????for(int?i=0;i<hits.length();i++) ??
  16. ????????{ ??
  17. ????????????Document?doc?=?hits.doc(i); ??
  18. ????????????System.out.println((i+1)+"."+doc.get("line")); ??
  19. ????????????result?=?doc.get("line"); ??
  20. ????????}?????????? ??
  21. ????} ??
  22. ????if(result!=null?&&?!result.equals("")) ??
  23. ????{ ??
  24. ????????String?[]?manyresult?=?result.split("?"); ??
  25. ????????for(int?i=0;i<manyresult.length;i++) ??
  26. ????????{ ??
  27. ????????????if(manyresult[i]!=null?&&?!manyresult[i].trim().equals("")) ??
  28. ????????????{ ??
  29. ????????????????list.add(manyresult[i]); ??
  30. ????????????} ??
  31. ????????} ??
  32. ????} ??
  33. ????return?list;???? ??
  34. }?????

	public List getTongYi(String searchword) throws Exception
	{
		List list = new ArrayList();
		Hits hits = null;    
		String queryString = searchword;    
		Query query = null;    
		String result = "";
		IndexSearcher searcher = new IndexSearcher("d:\\tong");    		   
		Analyzer analyzer = new StandardAnalyzer();    
		QueryParser qp = new QueryParser("line", analyzer);    
		query = qp.parse(queryString);    	        
		if (searcher != null)
		{    
		    hits = searcher.search(query);      
		    for(int i=0;i<hits.length();i++)
		    {
		    	Document doc = hits.doc(i);
		    	System.out.println((i+1)+"."+doc.get("line"));
		    	result = doc.get("line");
		    }          
		}
		if(result!=null && !result.equals(""))
		{
			String [] manyresult = result.split(" ");
			for(int i=0;i<manyresult.length;i++)
			{
				if(manyresult[i]!=null && !manyresult[i].trim().equals(""))
				{
					list.add(manyresult[i]);
				}
			}
		}
		return list;    
	}	

?我覺得做同義詞索引和搜索的時候最好用StandardAnalyzer,切成單字是最符合的。

寫了這么多,如果各位有更好的想法不妨發出來一起研究一下。

轉載于:https://blog.51cto.com/lucasyi/122803

相關閱讀

中國傳統新年團花的剪紙方法步驟圖解

窗花是貼在窗紙或窗戶玻璃上的剪紙,中國古老的傳統民間藝術之一。過去無論南方北方,春節期間都貼窗花。現在南方只結婚時才貼,春節一

佛珠手串打結圖解 三通穿法及中國結打法圖解

佛珠手串打結圖解,佛珠手串三通穿法及中國結打法圖解。非常經典的中國結藝DIY,相信學會后以后再給佛珠手串打結完全可以自己搞定,什

去水印,如何使用ps簡單方便的去除圖片中的水印

ps簡單方便的去除圖片中的水印一、修補工具1. 雙擊打開PS,按Ctrl+O打開要去除水印的圖片,選擇背景層,按Ctrl+J復制圖層或者將背景圖

右鍵中添加“管理員取得所有權”

打開記事本,復制下面的代碼:Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\*\shell\runas] @="管理員取得所有權" "

Excel2016中如何取消設置的篩選條件

在一張大型復雜的Excel表格中,可以通過強大的

(責任編輯:jjjccc)

關鍵詞:中

福彩3d字谜画谜藏机图