20、学习Lucene3.5索引之近实时搜索

/**
 * 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();
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值