你需要以下类来执行这个简单的索引与搜索的过程:
1、IndexWriter
2、IndexSearcher
3、IndexReader
4、Directory
5、Analyzer
6、Document
7、Field
8、Term
9、Query
10、TermQuery
11、Hits
接下来是对这些类的一个简短的浏览,针对它们在Lucene的角色,给出你粗略的概念。接下来我们开始介绍这些类。
IndexWriter类
IndexWriter是在索引过程中的中心组件。这个类创建一个新的索引并且添加文档到一个已有的索引中。你可以把IndexWriter想象成让你可以对索引进行写操作的对象,但是不能让你读取或搜索。此处使用线程同步的方式确保对象的使用高效、安全和减少内存的压力。
以下代码是从网络上粘贴过来的,代码可能会有错误,但是原理都是一样的,明白原理,什么都简单
MyIndexWriter中对IndexWriter的创建和回收:
public class MyIndexWriter { private static Analyzer analyzer = new PaodingAnalyzer(); private static IndexWriter indexWriter; private static ArrayList<Thread> threadList = new ArrayList<Thread>(); private static Directory dir = null; private MyIndexWriter() { } /** * * @function 创建indexWriter对象 * @param indexFilePath 创建索引的路径 * @param create 创建方式 * @return */ public static IndexWriter getInstance(String indexFilePath, boolean create) { synchronized (threadList) { if (indexWriter == null) { File indexFile = new File(indexFilePath); File lockfile = new File(indexFilePath + "/write.lock"); try { if (!indexFile.exists()) { indexFile.mkdir(); } dir = FSDirectory.open(indexFile); if (IndexWriter.isLocked(dir)) {// 判断目录是否已经锁住 IndexWriter.unlock(dir); } if (lockfile.exists()) { lockfile.delete(); } if (create) { indexWriter = new IndexWriter(dir, analyzer, true, MaxFieldLength.UNLIMITED); } else { if (IndexReader.indexExists(dir)) { indexWriter = new IndexWriter(dir, analyzer, false, MaxFieldLength.UNLIMITED); } else { indexWriter = new IndexWriter(dir, analyzer, true, MaxFieldLength.UNLIMITED); } } indexWriter.setMergeFactor(1000); indexWriter.setMaxFieldLength(Integer.MAX_VALUE); // 控制写入一个新的segment前在内存中保存的最大的document数目 indexWriter.setRAMBufferSizeMB(32); indexWriter.setMaxMergeDocs(200); } catch (CorruptIndexException e) { e.printStackTrace(); } catch (LockObtainFailedException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { indexFile=null; lockfile=null; } } if (!threadList.contains(Thread.currentThread())) threadList.add(Thread.currentThread()); return indexWriter; } } /** * * @function 关闭indexWriter对象 */ public static void close() { synchronized (threadList) { if (threadList.contains(Thread.currentThread())) threadList.remove(Thread.currentThread()); if (threadList.size() == 0) { try { if (indexWriter != null) { indexWriter.optimize(); indexWriter.commit(); indexWriter.close(); indexWriter = null; } if (dir != null) { dir.close(); } } catch (CorruptIndexException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } } }
实现过程:把普通对象转换成IndexWriter需要的Documents文档对象,IndexWriter是addDocument()方法把对象索引到文件。
/** * * @function 创建文档索引 * @param list 索引对象集合 */ private static void createDocIndex(List<SearchBean> list, String indexFilePath, boolean createMode) { try { // System.out.println(indexFilePath); indexWriter = MyIndexWriter.getInstance(indexFilePath + "/index", createMode); // 对所有的实体进行索引创建 for (SearchBean searchBean : list) { // 建立一个lucene文档 doc = new Document(); // 搜索内容 String id = searchBean.getId(); String title = searchBean.getTitle(); String content = searchBean.getContent(); String url = searchBean.getUrl(); String keyword = searchBean.getKeyword(); String time = searchBean.getTime(); String author = searchBean.getAuthor(); String module = searchBean.getModule(); String type = searchBean.getType(); // 添加主键至文档,不分词,不高亮。 doc.add(new Field("id", id, Field.Store.YES, Field.Index.NOT_ANALYZED, Field.TermVector.NO)); // 利用htmlparser得到处方html的纯文本 Parser parser = new Parser(); parser.setInputHTML(title); String titleText = parser.parse(null).elementAt(0) .toPlainTextString().trim(); // 添加到文档 doc.add(new Field("title", titleText, Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.WITH_POSITIONS_OFFSETS)); parser.setInputHTML(content); String contentText = parser.parse(null).elementAt(0) .toPlainTextString().trim(); // 添加到文档 doc.add(new Field("content", contentText, Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.WITH_POSITIONS_OFFSETS)); doc.add(new Field("url", url, Field.Store.YES, Field.Index.NOT_ANALYZED, Field.TermVector.NO)); doc.add(new Field("keyword", keyword, Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.WITH_POSITIONS_OFFSETS)); doc.add(new Field("time", time, Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.WITH_POSITIONS_OFFSETS)); doc.add(new Field("author", author, Field.Store.YES, Field.Index.NOT_ANALYZED, Field.TermVector.NO)); doc.add(new Field("module", module, Field.Store.YES, Field.Index.NOT_ANALYZED, Field.TermVector.NO)); doc.add(new Field("type", type, Field.Store.YES, Field.Index.NOT_ANALYZED, Field.TermVector.NO)); indexWriter.addDocument(doc); } } catch (Exception e) { logger.error(e.getMessage()); } finally { MyIndexWriter.close(); } }
删除文档是需要保证的是数据的唯一性,一般把不分词的域作为判断的依据,如果一个域还不能完全保证数据的唯一性,那就需要多个域的组合判断。
下文教你使用组合域删除唯一对象,这是Lucene教程里面没有涉及到的内容,目前使用正常,如果你在使用过程中遇到问题请查阅BooleanQuery的使用方式。
/** * * @function 删除 * @param id * 资源Id */ public static void deleteDocument(Long id, String className) { DictItemBiz dictItemBiz = (DictItemBiz) ac.getBean("dictItemBiz"); TermQuery idQuery = new TermQuery(new Term("id", id + ""));//删除的第一个域 TermQuery resourceQuery = new TermQuery(new Term("type", dictItemBiz.getItemByDictMarkAndItemVal("search_type", className) .getItemId() + ""));//删除的第二个域 BooleanQuery bQuery = new BooleanQuery(); bQuery.add(idQuery, BooleanClause.Occur.MUST); bQuery.add(resourceQuery, BooleanClause.Occur.MUST); // 自己的模块的索引更新 String indexFilePath = FILE_PATH +ConfigurationManager.getInstance().getPropertiesValue( "common.properties", className + "IndexFilePath") + "/index"; try { deleteDocument(bQuery, indexFilePath); } catch (Exception e) { logger.error(e.getMessage()); } finally { // 重新合并索引 createModuleIndexAndSpellCheck(); } } /** * * @function 删除文档 * @param bQuery * @param indexFilePath * @throws Exception */ private static void deleteDocument(BooleanQuery bQuery, String indexFilePath) { indexWriter = MyIndexWriter.getInstance(indexFilePath, false); try { indexWriter.deleteDocuments(bQuery); } catch (Exception e) { logger.error(e.getMessage()); } finally { MyIndexWriter.close(); } }
更新索引是一个删除再添加的过程,掌握添加和删除的技巧后,直接运用于更新即可。
使用IndexWriter更新索引合并多个索引文件
在创建索引的过程中,经常会使用不同的情况创建多个不同的索引文件,搜索的时候有时会搜索整个内容,这就需要使用合并索引的技术,虽然你可以创建索引的时候生成一个包含全部内容的索引文件,但是显然这是不合理的方法,这样会浪费大量的内存,在内容较多的情况下极易内存溢出。使用这种合并多个索引的方式使你的程序更加灵活可控。
合并索引文件的关键方法:addIndexesNoOptimize 但是现在Deprecated.use addIndexes(Directory...)
instead
/** * * @function 合并索引文件 * @param fromFilePaths 需要合并的索引文件路径 * @param toFilePath 合并完成的索引文件路径 */ private static void mergeIndex(String[] fromFilePaths, String toFilePath) { IndexWriter indexWriter = null; File file = null; FSDirectory fsd = null; try { // System.out.println("正在合并索引文件!\t "); indexWriter = MyIndexWriter.getInstance(toFilePath + "/index", false); for (String fromFilePath : fromFilePaths) { file = new File(fromFilePath + "/index"); if (file.exists()) { fsd = FSDirectory.open(file); indexWriter.addIndexesNoOptimize(fsd); } } } catch (Exception e) { e.printStackTrace(); logger.error(e.getMessage()); } finally { try { MyIndexWriter.close(); if (fsd != null) { fsd.close(); } file = null; } catch (Exception e) { e.printStackTrace(); logger.error(e.getMessage()); } } }