/** * lucene实现近实时搜索: * lucene通过NRTManager这个类来实现近实时搜索。所谓近实时搜索即在索引发生改变时,通过线程跟踪,在相对很短的时间反映 * 给用户程序的调用。 * NRTManager通过管理IndexWriter对象,并将IndexWriter的一些方法(增删改)例如addDocument,deleteDocument等方法暴露给 * 客户调用。它的操作全部在内存里面,所以如果你不调用IndexWriter的commit方法,通过以上的操作,用户硬盘里面的索引库是不会 * 变化的,所以你每次更新完索引库请记得commit()掉(注意:一般是开发人员自己控制多长时间提交一次),这样才能将变化的索引 * 一起写到硬盘中,实现索引更新后的同步。 * 用户每次获取最新索引(IndexSearcher),可以通过两种方式: * 第一种是通过调用NRTManagerReopenThread对象,该线程负责实时跟踪索引内存的变化,每次变化就“自动”调用 maybeReopen()方法, * 保持最新代索引,打开一个新的IndexSearcher对象,而用户所要的IndexSearcher对象是NRTManager通过调用getSearcherManager() * 方法获得SearcherManager对象,然后通过SearcherManager对象获取IndexSearcher对象返回给客户使用,用户使用完之 后调用 * SearcherManager的release释放IndexSearcher对象,最后记得关闭NRTManagerReopenThread; * 第二种方式是不通过NRTManagerReopenThread对象,而是“手动”调用NRTManager的maybeReopen()方法来获取最新的IndexSearcher * 对象来获取最新索引 * * NRTManager使用基本流程: * 1.首先创建并初始化NRTManager对象 * 2.创建并初始化NRTManagerReopenThread线程(用该线程周期性地查看索引是否发生改变及是否需要重新打开索引) * 3.通过NRTManager的getSearcherManager()方法获取SearcherManager * 4.通过NRTManager对索引进行增、删、改、查操作 * 5.通过dSearcherManager的acquire()方法获取InexSearcher对象,执行查询操作。查询完毕后,调用SearcherManager的 * release()释放IndexSearcher对象 */ public class NRTSearch { private String[] ids = {"1","2","3","4","5","6"}; private String[] emails = {"aa@aa.com","bb@bb.com","cc@aa.com","dd@dd.com","ee@ee.com","ff@aa.com"}; private String[] content = {"My name is aa","My name is bb, aa is my brother's name ","My name is cc","My name is dd" ,"My name is ee","My name is ff, aa is my sister's name"}; private int[] attaches = {1,2,3,4,5,6}; private String[] names = {"aa","bb","cc","dd","ee","ff"}; private Date[] dates = null; private Directory directory = null; /*----------------------------------------近实时搜索------------------------------------------*/ /** * IndexWriter设置成单例 */ private static IndexWriter indexWriter = null; //线程安全 private NRTManager nrtManager = null; private SearcherManager searcherManager = null; /*----------------------------------------近实时搜索------------------------------------------*/ public NRTSearch(){ initDates(); try { /** * 创建索引目录 * FSDirectory.open():根据当前环境,选择最好的打开方式 */ directory = FSDirectory.open(new File("e:/lucene/index02")); /*----------------------------------------近实时搜索------------------------------------------*/ indexWriter = new IndexWriter(directory, new IndexWriterConfig(Version.LUCENE_35, new StandardAnalyzer(Version.LUCENE_35))); /** * 初始化NRTManager对象: * 参数一:IndexWriter * 参数二:ExecutorService 线程池 * 参数三:SearcherWarmer(一旦重新打开索引,就会调用这个该接口) * 备注:NRTManager对象封装了IndexWriter对象的大部分方法(增、删、改、查),若要实现近实时搜索,必须调用封装 * 好的方法,不能再通过IndexWriter来调用相关方法 */ nrtManager = new NRTManager(indexWriter, Executors.newCachedThreadPool(), new SearcherWarmer() { /** * 索引一更新就要重新获取searcher,那获取searcher的时候就会调用maybeReopen()方法 * 执行maybeReopen()的时候会执行warm方法,在这里可以对资源等进行控制 * 备注:参见前面说明,有两种方式来调用maybeReopen() */ @Override public void warm(IndexSearcher s) throws IOException { System.out.println("重新打开索引"); } }); /** * 以下是自动获取最新索引 */ /** * 启动NRTManager的Reopen线程,NRTManagerReopenThread会每隔25秒去检测一下索引是否更新并判断是否需要重新 * 打开writer(0.025代表25秒) * NRTManagerReopenThread(NRTManager manager, double targetMaxStaleSec, double targetMinStaleSec)参数解析 * 参数一:NRTManager对象 * 参数二:double targetMaxStaleSec 检测索引是否更新并判断是否需要重新打开writer的最大时间 * 参数三:double targetMinStaleSec 最小时间 */ NRTManagerReopenThread reopenThread = new NRTManagerReopenThread(nrtManager, 5.0, 0.025); //设置为守护线程(优先级较低,在没有用户线程可服务时会自动离开) reopenThread.setDaemon(true); reopenThread.setName("NrtManager Reopen Thread"); //启动该线程 reopenThread.start(); /** * 参数:boolean applyAllDeletes * 为true,若有删除操作,则在下一次重新打开索引之后,会查询不出来已经被删除的索引; * 为false,被删除的索引依然存在 */ searcherManager = nrtManager.getSearcherManager(true); } catch (IOException e) { e.printStackTrace(); } /*----------------------------------------近实时搜索------------------------------------------*/ } private void initDates() { dates = new Date[ids.length]; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd"); try { dates[0] = sdf.parse("2018-01-01"); dates[1] = sdf.parse("2017-01-01"); dates[2] = sdf.parse("2016-01-01"); dates[3] = sdf.parse("2015-01-01"); dates[4] = sdf.parse("2014-01-01"); dates[5] = sdf.parse("2013-01-01"); } catch (ParseException e) { e.printStackTrace(); } } public void index(){ try { //每次创建新的索引之前,先将以前的索引删除掉 indexWriter.deleteAll(); //创建Document,并添加域 Document document = null; for (int i=0;i<ids.length;i++){ document = new Document(); //存储在硬盘,但不分词,不加权 document.add(new Field("id",ids[i],Field.Store.YES,Field.Index.NOT_ANALYZED_NO_NORMS)); //存储在硬盘,但不分词,但加权 document.add(new Field("email",emails[i],Field.Store.YES,Field.Index.NOT_ANALYZED)); //不存储在硬盘,但分词、加权 document.add(new Field("content",content[i],Field.Store.NO,Field.Index.ANALYZED)); //存储在硬盘,但不分词,不加权 document.add(new Field("name",names[i],Field.Store.YES,Field.Index.NOT_ANALYZED_NO_NORMS)); /** * 为数字和日期建立索引: * NumericField(String name, int precisionStep, Store store, boolean index) * 为这两项建立索引有专门的方法,NumericField(),其中第四个参数不再是索引的方式,而是是否进行索引 */ //为数字建立索引 NumericField numericField = new NumericField("attach", Field.Store.YES, true); numericField.setIntValue(attaches[i]); document.add(numericField); /** * 为日期建立索引: * 核心思想,将Date对象转换成数字 */ long time = dates[i].getTime();//将日期转换成long型 //创建NumericField对象 NumericField dateNumericField = new NumericField("date", Field.Store.YES, true); //设置索引值 dateNumericField.setLongValue(time); document.add(dateNumericField); /*----------------------------------------近实时搜索------------------------------------------*/ //添加索引文档(这里不再用IndexWriter,而是用NRTManager) nrtManager.addDocument(document); /*----------------------------------------近实时搜索------------------------------------------*/ } } catch (IOException e) { e.printStackTrace(); } } /** * 搜索 */ public void search(){ /*----------------------------------------近实时搜索------------------------------------------*/ //创建IndexSearcher对象(不是用IndexReader创建IndexSearcher,而是用SearcherManager创建IndexSearcher) IndexSearcher indexSearcher = searcherManager.acquire(); /*----------------------------------------近实时搜索------------------------------------------*/ //创建TermQuery(精确搜索对象) TermQuery termQuery = new TermQuery(new Term("content", "name")); //搜索 try { TopDocs topDocs = indexSearcher.search(termQuery, 10); ScoreDoc[] scoreDocs = topDocs.scoreDocs; Calendar calendar = Calendar.getInstance(); for (ScoreDoc scoreDoc:scoreDocs){ Document document = indexSearcher.doc(scoreDoc.doc); //将数字转换成日期(因为前面是将日期转换成数字进行索引的) SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd"); String time = document.get("date"); calendar.setTimeInMillis(Long.valueOf(time)); String date = sdf.format(calendar.getTime()); System.out.println("("+scoreDoc.doc+")"+document.get("name")+"["+document.get("email")+"]-->"+document.get("id") + "," +document.get("attach") + "," +date); } } catch (IOException e) { e.printStackTrace(); }finally { /*----------------------------------------近实时搜索------------------------------------------*/ try { //不再是关闭IndexSearcher,而是调用searcherManager.release()释放掉IndexSearcher searcherManager.release(indexSearcher); } catch (IOException e) { e.printStackTrace(); } /*----------------------------------------近实时搜索------------------------------------------*/ } } /** * 删除索引 */ public void deleteIndex(){ try { /*----------------------------------------近实时搜索------------------------------------------*/ nrtManager.deleteDocuments(new Term("id", "1")); /*----------------------------------------近实时搜索------------------------------------------*/ } catch (IOException e) { e.printStackTrace(); } } /** * 更新 */ public void updateIndex(){ try { /** * */ Document document = new Document(); document.add(new Field("id","8",Field.Store.YES,Field.Index.NOT_ANALYZED_NO_NORMS)); //存储在硬盘,但不分词,但加权 document.add(new Field("email",emails[0],Field.Store.YES,Field.Index.NOT_ANALYZED)); //不存储在硬盘,但分词、加权 document.add(new Field("content",content[0],Field.Store.NO,Field.Index.ANALYZED)); //存储在硬盘,但不分词,不加权 document.add(new Field("name",names[0],Field.Store.YES,Field.Index.NOT_ANALYZED_NO_NORMS)); /*----------------------------------------近实时搜索------------------------------------------*/ nrtManager.updateDocument(new Term("id", "2"), document); /*----------------------------------------近实时搜索------------------------------------------*/ } catch (IOException e) { e.printStackTrace(); } } /** * 提交内存中变动的索引到硬盘: * NRTManager中所有的添加修改删除的操作只在内存中生效,然后使用内存中的索引信息在搜索时能起到效果。 * 但是过一段时间累计到一定程序需要进行writer.commit(),将索引更新到硬盘中,这样下一次重新搜索才是最新的索引。 * NRTManage就是这样的功能,把更新的数据存储在内存中,但是lucene搜索的时候也可以搜索到,需要 * writer进行commit才会把索引更新到硬盘中 */ public void commit(){ try { indexWriter.commit(); } catch (IOException e) { e.printStackTrace(); } } }
20、学习Lucene3.5索引之近实时搜索
最新推荐文章于 2020-05-06 20:23:46 发布