Lucene+分词器精确提取用户自定义关键词(Lucene版本3.6)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/EchoYY/article/details/78291868

此篇博客的Lucene版本是3.6.0,若您的Lucene版本为5.X可以移步到此博文http://blog.csdn.net/echoyy/article/details/78468225


在分词的过程中,有时候需要只提取词典里自定义好的关键词,而传统的分词工具如(IKanalyzer)好像并不支持此功能


CSDN论坛里有人给出解决方法,是采用Lucene的TermQuery方法来进行索引的检索功能


然而我自己在调用TermQuery时候反而什么都检索不出来


最后发现是索引的建立问题


最开始使用的是流的方式读词典文件就像酱样doc.add(new Field("contents",new FileReader(files)));


然后使用TermQuery就gg了,因此唯结果论话推测这种方式在建立索引的时候应该是对文本进行了拆解,也就是说比如词典里存的是“你好”,这种方式在建立索引时将“你好”拆成了“你”和“好”,导致TermQuery在对“你好”进行检索的过程中检索不到值。


解决方案是先用BufferReader来读取词典中的文本,将其存到一个List<String> content中,后续循环取List中的值调用doc.add(new Field("contents",content,Field.Store.YES, Field.Index.NOT_ANALYZED));方法


注意这里的域索引选项(Field.Index.*)使用的是Index.NOT_ANALYZED参数,这是由于该参数实际上将域值作为单一语汇单元并使之能被搜索。适用于索引那些不能被分解的域值比如URL、文件路径、日期、人名、电话号码等。


然后,搞定


最后是索引和检索的代码

1、建立索引

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;

/**
 * 类简介: ①简单的向文档里写索引; ②在根据索引读取文档; ③运用路径来找被索引的文档,找到返回结果
 */
public class Indexer {

	// 写索引的实例到指定目录下
	private IndexWriter writer;

	/**
	 * 构造方法:为了实例化IndexWriter
	 */
	public Indexer(String indexDir) throws Exception {

		// 得到索引所在目录的路径
		Directory dir = FSDirectory.open(new File(indexDir));

		// 实例化分析器
		Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_35);

		// 实例化IndexWriterConfig
		IndexWriterConfig con = new IndexWriterConfig(Version.LUCENE_35, analyzer);

		// 实例化IndexWriter
		writer = new IndexWriter(dir, con);

	}

	/**
	 * 关闭写索引
	 * 
	 * @throws Exception
	 */
	public void close() throws Exception {

		writer.close();
	}

	/**
	 * 索引指定目录的所有文件
	 * 
	 * @throws Exception
	 */
	public int index(String dataDir) throws Exception {

		// 定义文件数组,循环得出要加索引的文件
		File[] file = new File(dataDir).listFiles();

		for (File files : file) {

			// 从这开始,对每个文件加索引
			indexFile(files);
		}

		// 返回索引了多少个文件,有几个文件返回几个
		return writer.numDocs();

	}

	/**
	 * 索引指定文件
	 * 
	 * @throws Exception
	 */
	private void indexFile(File files) throws Exception {

		System.out.println("索引文件:" + files.getCanonicalPath());

		// 索引要一行一行的找,,在数据中为文档,所以要得到所有行,即文档
		Document document = getDocument(files);

		// 开始写入,就把文档写进了索引文件里去了;
		writer.addDocument(document);

	}

	/**
	 * 获得文档,在文档里在设置三个字段
	 * 
	 * 获得文档,相当于数据库里的一行
	 * 
	 * @throws Exception
	 */
	private Document getDocument(File files) throws Exception {

		// 实例化Document
		Document doc = new Document();
		// add():把设置好的索引加到Document里,以便在确定被索引文档。

		List<String> contents = this.getContent(files);
		for (String content : contents) {
			doc.add(new Field("contents", content, Field.Store.YES, Field.Index.NOT_ANALYZED));
		}
		// 返回document
		return doc;
	}

	private List<String> getContent(File files) {
		List<String> strList = new ArrayList<String>();
		try {
			InputStream stream = new FileInputStream(files);
			String code = "UTF-8";
			BufferedReader br = new BufferedReader(new InputStreamReader(stream, code));
			String str = br.readLine();
			while (str != null) {
				strList.add(str);
				str = br.readLine();
			}
			br.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return strList;
	}

	// 开始测试写入索引
	public static void main(String[] args) {
		// 索引指定的文档路径
		String indexDir = "./Index/Keyword";
		// 被索引数据的路径
		File dataDir = new File("D:\\workspace\\IKTest\\src\\ext.dic");
		// 写索引
		Indexer indexer = null;
		try {
			// 通过索引指定的路径,得到indexer
			indexer = new Indexer(indexDir);
			// 将要索引的数据路径(int:因为这是要索引的数据,有多少就返回多少数量的索引文件)
			indexer.indexFile(dataDir);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				indexer.close();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}


2、搜索关键字

import java.io.File;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;

/**
 * 
 * 通过索引字段来读取文档
 *
 */
public class SearchKeyword {
	
	private  static final String indexDir = "./Index/Keyword";

	public boolean search(String keyword)throws Exception{
		
		//得到读取索引文件的路径
		Directory dir=FSDirectory.open(new File(indexDir));
		
		//通过dir得到的路径下的所有的文件
		IndexReader reader=IndexReader.open(dir);
		
		//建立索引查询器
		IndexSearcher is=new IndexSearcher(reader);
		
		//实例化分析器
		Analyzer analyzer=new StandardAnalyzer(Version.LUCENE_35); 
		//建立查询解析器
		/**
		 * 第一个参数是要查询的字段;
		 * 第二个参数是分析器Analyzer
		 * */
		QueryParser parser=new QueryParser(Version.LUCENE_35, "contents", analyzer);
		//根据传进来的p查找
//		Query query=parser.parse(keyword);
		TermQuery query = new TermQuery(new Term("contents",keyword));
		//开始查询
		/**
		 * 第一个参数是通过传过来的参数来查找得到的query;
		 * 第二个参数是要出查询的行数
		 * */
		TopDocs hits=is.search(query, 10);
		boolean flag = false;
		if(hits.totalHits>0) {
			flag = true;
		}
		reader.close();
		return flag;
	}
}


3、main函数
import java.io.StringReader;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.wltea.analyzer.lucene.IKAnalyzer;

public class Main {
	public static void main(String[] args) {
		String keyWord = "这是一整个关键词德玛西亚";
		IKAnalyzer analyzer = new IKAnalyzer(true);
		try {
			printAnalysisResult(analyzer, keyWord);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 打印出给定分词器的分词结果
	 * 
	 * @param analyzer
	 *            分词器
	 * @param keyWord
	 *            关键词
	 * @throws Exception
	 */
	private static void printAnalysisResult(Analyzer analyzer, String keyWord)
			throws Exception {
		System.out.println("["+keyWord+"]分词效果如下");
		TokenStream tokenStream = analyzer.tokenStream("content",
				new StringReader(keyWord));
		tokenStream.addAttribute(CharTermAttribute.class);
		SearchKeyword sk = new SearchKeyword();
		while (tokenStream.incrementToken()) {
			CharTermAttribute charTermAttribute = tokenStream
					.getAttribute(CharTermAttribute.class);
			if(sk.search(charTermAttribute.toString())==true) {
				System.out.println(charTermAttribute.toString());
			}

		}
	}
}


4、用户自定义关键词



5、运行结果


可以看到“这是一整个关键词”被精确索引出来了,

而“德玛西亚”关键词由于在字典中存储的是“*德玛西亚*”,不满足精确搜索所以并没有被切分出来


猜你喜欢

转载自blog.csdn.net/EchoYY/article/details/78291868