Lucene學習之中文查詢問題的解決

在構建文檔庫之後,絕大多數的查詢都是基於中文進行查詢。使用前面的例子,進行測試時,會發現這樣問題:

使用關鍵詞“微信”進行搜索時,能夠搜索到沒有“微信”這個詞出現,但是有“微”和“信”這兩個字出現的文檔。造成這種錯誤搜索的原因是,Lucene標準的分析器在分析文檔以及查詢關鍵詞時,對於英文是基於單詞分詞的,但是對於中文,是基於漢字進行分詞的,所以就造成了上述的查詢問題。爲了解決這個問題,我們只需要將Lucene的標準分析器換成中文的分析器就可以了,IK Analyzer基於lucene實現的分詞開源框架具有如下特性:

 

  • 算法採用“正向迭代最細粒度切分算法”, 支持細粒度和最大詞長兩種分詞 方式,速度最大支持 80W 字/秒(1600KB/秒) 。
  •  支持多子處理器分析模式:中文、數字、字母,併兼容日文、韓文。
  • 較小的內存佔用,優化詞庫佔有空間,用戶可自定義擴展詞庫。 採用歧義分析算法優化查詢關鍵字的搜索排列組
  •  基於lucene 的擴展實現,提高 lucene 檢索命中率。

比如對於“基於java語言開發的輕量級的中文分詞工具包”,使用IK Analyzer分析後的結果爲:

基於|java|語言|開發|的|輕量級|的|中文|分詞|工具包|

基本上符合中文的分詞,而使用Lucene標準的分析器,則分析的結果就是逐字分開。所以使用IK Analyzer作爲分析器構建中文文檔庫時,不但能減少索引文件大小,還可以加快搜索速度。在使用時,只需要將構建索引時和查詢時的分析器修改爲IK Analyzer,就可以了。使用步驟如下: 

 

1.在http://code.google.com/p/ik-analyzer/downloads/list下載IK Analyzer 2012FF_hf1.zip(該版本與

   Lucene4.x兼容)

2.解壓下載文件在classpath加入IKAnalyzer2012FF_u1.jar,並且將stopword.dic和IKAnalyzer.cfg.xml文件

   添加到src的根路徑下(保證編譯時這兩個文件copy到classes的根路徑下)。

3.在代碼中使用IK Analyzer作爲分析器

 

代碼如下,爲了便於調試,此處將全部代碼貼出:

 

package com.hsdl.lucene;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.IndexWriterConfig.OpenMode;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.store.LockObtainFailedException;
import org.apache.lucene.util.Version;
import org.apache.tika.Tika;
import org.wltea.analyzer.lucene.IKAnalyzer;

public class LuceneDemo2 {
	private static String contentFieldName = "content";
	private static Tika tika = new Tika();
	public static void main(String[] args) {
		//Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_45);
		//使用IKAnalyzer構建分析器
		Analyzer analyzer = new IKAnalyzer(true);
		try {
			String docPath = "D:/work/lucene/tika/doc";
			String indexPath = "D:/work/lucene/tika/index";
			//創建索引
			createIndex(analyzer, indexPath, docPath);
			//搜索
			search(analyzer, indexPath, "微信");
		} catch (CorruptIndexException e) {
			e.printStackTrace();
		} catch (LockObtainFailedException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
	}

	/**
	 * 創建索引
	 * 
	 * @param analyzer
	 * @param indexPath
	 * @param docPath
	 * @throws IOException
	 * @throws CorruptIndexException
	 * @throws LockObtainFailedException
	 */
	private static void createIndex(Analyzer analyzer, String indexPath,
			String docPath) throws IOException, CorruptIndexException,
			LockObtainFailedException {
		IndexWriter iwriter;
		Directory directory = FSDirectory.open(new File(indexPath));
		// 配置IndexWriterConfig
		IndexWriterConfig iwConfig = new IndexWriterConfig(Version.LUCENE_45,
				analyzer);
		iwConfig.setOpenMode(OpenMode.CREATE_OR_APPEND);
		iwriter = new IndexWriter(directory, iwConfig);
		File file = new File(docPath);
		indexDocs(iwriter, file);
		iwriter.close();
	}

	/**
	 * 搜索
	 * 
	 * @param analyzer
	 * @param indexPath
	 * @param queryStr
	 * @throws CorruptIndexException
	 * @throws IOException
	 * @throws ParseException
	 */
	private static void search(Analyzer analyzer, String indexPath,
			String queryStr) throws CorruptIndexException, IOException,
			ParseException {
		Directory directory = FSDirectory.open(new File(indexPath));
		// 搜索過程**********************************
		// 實例化搜索器
		IndexReader ireader = DirectoryReader.open(directory);
		IndexSearcher isearcher = new IndexSearcher(ireader);

		// 使用QueryParser查詢分析器構造Query對象
		QueryParser qp = new QueryParser(Version.LUCENE_45, contentFieldName, analyzer);
		qp.setDefaultOperator(QueryParser.AND_OPERATOR);
		Query query = qp.parse(queryStr);

		// 搜索相似度最高的5條記錄
		TopDocs topDocs = isearcher.search(query, 10);
		System.out.println("命中:" + topDocs.totalHits);
		// 輸出結果
		ScoreDoc[] scoreDocs = topDocs.scoreDocs;
		System.out.println(scoreDocs.length);
		for (int i = 0; i < scoreDocs.length; i++) {
			Document targetDoc = isearcher.doc(scoreDocs[i].doc);
			System.out.println("內容:" + targetDoc.toString());
			System.out.println(targetDoc.get("fileName") + "["
					+ targetDoc.get("path") + "]");
		}
	}
	/**
	 * 根據指定存放內容的文件或目錄創建索引
	 * @param iwriter
	 * @param file
	 * @throws IOException
	 */
	public static void indexDocs(IndexWriter iwriter, File file) throws IOException {
		if (file.canRead())
			if (file.isDirectory()) {
				String[] files = file.list();

				if (files != null)
					for (int i = 0; i < files.length; i++)
						indexDocs(iwriter, new File(file, files[i]));
			} else {
				Document doc = null;
				FileInputStream fis=null;
				try {
					doc = new Document();
					doc.add(new StringField("ID", "10000", Field.Store.YES));
					fis = new FileInputStream(file);
					//此處添加文件內容時,需要根據tika獲取Reader對象
					doc.add(new TextField(contentFieldName, tika.parse(file)));
					doc.add(new StringField("fileName", file.getName(),
							Field.Store.YES));
					doc.add(new StringField("path", file.getAbsolutePath(),
							Field.Store.YES));
					iwriter.addDocument(doc);
				} finally {
					if(fis!=null){
						fis.close();
					}
				}
			}
	}
}

 

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