lucene-分页查询

  1. 两种方式
    分页查询是很常见的需求,比如要查询第10页,每页10条数据。
    Lucene 分页通常来讲有两种方式:
    第一种是把100条数据查出来,然后取最后10条。 优点是快,缺点是对内存消耗大。
    第二种是把第90条查询出来,然后基于这一条,通过searchAfter方法查询10条数据。 优点是内存消耗小,缺点是比第一种更慢
  2.  第一种
    	private static ScoreDoc[] pageSearch1(Query query, IndexSearcher searcher, int pageNow, int pageSize)
    			throws IOException {
    		TopDocs topDocs = searcher.search(query, pageNow*pageSize);
    		 System.out.println("查询到的总条数\t"+topDocs.totalHits);
    		 ScoreDoc [] alllScores = topDocs.scoreDocs;
    		 List<ScoreDoc> hitScores = new ArrayList<>();
    		
    		 
    		 int start = (pageNow -1)*pageSize ;
    		 int end = pageSize*pageNow;
    		 for(int i=start;i<end;i++)
    			 hitScores.add(alllScores[i]);
    		
    		
    		 ScoreDoc[] hits = hitScores.toArray(new ScoreDoc[]{});
    		return hits;
    	}
    一共查出 pageNow*pageSize条,然后取最后pageSize条
    public class TestLucene {
     
        public static void main(String[] args) throws Exception {
            // 1. 准备中文分词器
            IKAnalyzer analyzer = new IKAnalyzer();
            // 2. 索引
            Directory index = createIndex(analyzer);
     
            // 3. 查询器
             
                String keyword = "手机";
                System.out.println("当前关键字是:"+keyword);
                Query query = new QueryParser( "name", analyzer).parse(keyword);
     
                // 4. 搜索
                IndexReader reader = DirectoryReader.open(index);
                IndexSearcher searcher=new IndexSearcher(reader);
                int pageNow = 1;
                int pageSize = 10;
                 
                ScoreDoc[] hits = pageSearch1(query, searcher, pageNow, pageSize);
                 
                // 5. 显示查询结果
                showSearchResults(searcher, hits,query,analyzer);
                // 6. 关闭查询
                reader.close();
             
        }
     
        private static ScoreDoc[] pageSearch1(Query query, IndexSearcher searcher, int pageNow, int pageSize)
                throws IOException {
            TopDocs topDocs = searcher.search(query, pageNow*pageSize);
             System.out.println("查询到的总条数\t"+topDocs.totalHits);
             ScoreDoc [] alllScores = topDocs.scoreDocs;
     
             List<ScoreDoc> hitScores = new ArrayList<>();
             
             int start = (pageNow -1)*pageSize ;
             int end = pageSize*pageNow;
             for(int i=start;i<end;i++)
                 hitScores.add(alllScores[i]);
             
             ScoreDoc[] hits = hitScores.toArray(new ScoreDoc[]{});
            return hits;
        }
         
        private static void showSearchResults(IndexSearcher searcher, ScoreDoc[] hits, Query query, IKAnalyzer analyzer) throws Exception {
            System.out.println("找到 " + hits.length + " 个命中.");
     
            SimpleHTMLFormatter simpleHTMLFormatter = new SimpleHTMLFormatter("<span style='color:red'>", "</span>");
            Highlighter highlighter = new Highlighter(simpleHTMLFormatter, new QueryScorer(query));
     
            System.out.println("找到 " + hits.length + " 个命中.");
            System.out.println("序号\t匹配度得分\t结果");
            for (int i = 0; i < hits.length; ++i) {
                ScoreDoc scoreDoc= hits[i];
                int docId = scoreDoc.doc;
                Document d = searcher.doc(docId);
                List<IndexableField> fields= d.getFields();
                System.out.print((i + 1) );
                System.out.print("\t" + scoreDoc.score);
                for (IndexableField f : fields) {
     
                    if("name".equals(f.name())){
                        TokenStream tokenStream = analyzer.tokenStream(f.name(), new StringReader(d.get(f.name())));
                        String fieldContent = highlighter.getBestFragment(tokenStream, d.get(f.name()));
                        System.out.print("\t"+fieldContent);
                    }
                    else{
                        System.out.print("\t"+d.get(f.name()));
                    }
                }
                System.out.println("<br>");
            }
        }
     
        private static Directory createIndex(IKAnalyzer analyzer) throws IOException {
            Directory index = new RAMDirectory();
            IndexWriterConfig config = new IndexWriterConfig(analyzer);
            IndexWriter writer = new IndexWriter(index, config);
            String fileName = "140k_products.txt";
            List<Product> products = ProductUtil.file2list(fileName);
            int total = products.size();
            int count = 0;
            int per = 0;
            int oldPer =0;
            for (Product p : products) {
                addDoc(writer, p);
                count++;
                per = count*100/total;
                if(per!=oldPer){
                    oldPer = per;
                    System.out.printf("索引中,总共要添加 %d 条记录,当前添加进度是: %d%% %n",total,per);
                }
                 
                if(per>10)
                    break;
                 
            }
            writer.close();
            return index;
        }
     
        private static void addDoc(IndexWriter w, Product p) throws IOException {
            Document doc = new Document();
            doc.add(new TextField("id", String.valueOf(p.getId()), Field.Store.YES));
            doc.add(new TextField("name", p.getName(), Field.Store.YES));
            doc.add(new TextField("category", p.getCategory(), Field.Store.YES));
            doc.add(new TextField("price", String.valueOf(p.getPrice()), Field.Store.YES));
            doc.add(new TextField("place", p.getPlace(), Field.Store.YES));
            doc.add(new TextField("code", p.getCode(), Field.Store.YES));
            w.addDocument(doc);
        }
    }
  3.  第二种
    private static ScoreDoc[] pageSearch2(Query query, IndexSearcher searcher, int pageNow, int pageSize)
    			throws IOException {
            int start = (pageNow - 1) * pageSize;
            if(0==start){
            	TopDocs topDocs = searcher.search(query, pageNow*pageSize);
            	return topDocs.scoreDocs;
            }
            // 查询数据, 结束页面自前的数据都会查询到,但是只取本页的数据
            TopDocs topDocs = searcher.search(query, start);
            //获取到上一页最后一条
            
            ScoreDoc preScore= topDocs.scoreDocs[start-1];
            //查询最后一条后的数据的一页数据
            topDocs = searcher.searchAfter(preScore, query, pageSize);
            return topDocs.scoreDocs;
            
    	}
    首先是边界条件,如果是第一页,就直接查询了。
    如果不是第一页,那么就取start-1那一条,然后再根据它通过searchAfter 来查询
    public class TestLucene {
     
        public static void main(String[] args) throws Exception {
            // 1. 准备中文分词器
            IKAnalyzer analyzer = new IKAnalyzer();
            // 2. 索引
            Directory index = createIndex(analyzer);
     
            // 3. 查询器
             
                String keyword = "手机";
                System.out.println("当前关键字是:"+keyword);
                Query query = new QueryParser( "name", analyzer).parse(keyword);
     
                // 4. 搜索
                IndexReader reader = DirectoryReader.open(index);
                IndexSearcher searcher=new IndexSearcher(reader);
                int pageNow = 1;
                int pageSize = 10;
                 
                ScoreDoc[] hits = pageSearch2(query, searcher, pageNow, pageSize);
                 
                // 5. 显示查询结果
                showSearchResults(searcher, hits,query,analyzer);
                // 6. 关闭查询
                reader.close();
             
        }
     
        private static ScoreDoc[] pageSearch1(Query query, IndexSearcher searcher, int pageNow, int pageSize)
                throws IOException {
            TopDocs topDocs = searcher.search(query, pageNow*pageSize);
             System.out.println("查询到的总条数\t"+topDocs.totalHits);
             ScoreDoc [] alllScores = topDocs.scoreDocs;
     
             List<ScoreDoc> hitScores = new ArrayList<>();
             
             int start = (pageNow -1)*pageSize ;
             int end = pageSize*pageNow;
             for(int i=start;i<end;i++)
                 hitScores.add(alllScores[i]);
             
             ScoreDoc[] hits = hitScores.toArray(new ScoreDoc[]{});
            return hits;
        }
         
        private static ScoreDoc[] pageSearch2(Query query, IndexSearcher searcher, int pageNow, int pageSize)
                throws IOException {
             
            int start = (pageNow - 1) * pageSize;
            if(0==start){
                TopDocs topDocs = searcher.search(query, pageNow*pageSize);
                return topDocs.scoreDocs;
            }
            // 查询数据, 结束页面自前的数据都会查询到,但是只取本页的数据
            TopDocs topDocs = searcher.search(query, start);
            //获取到上一页最后一条
             
            ScoreDoc preScore= topDocs.scoreDocs[start-1];
     
            //查询最后一条后的数据的一页数据
            topDocs = searcher.searchAfter(preScore, query, pageSize);
            return topDocs.scoreDocs;
             
        }
     
        private static void showSearchResults(IndexSearcher searcher, ScoreDoc[] hits, Query query, IKAnalyzer analyzer) throws Exception {
            System.out.println("找到 " + hits.length + " 个命中.");
     
            SimpleHTMLFormatter simpleHTMLFormatter = new SimpleHTMLFormatter("<span style='color:red'>", "</span>");
            Highlighter highlighter = new Highlighter(simpleHTMLFormatter, new QueryScorer(query));
     
            System.out.println("找到 " + hits.length + " 个命中.");
            System.out.println("序号\t匹配度得分\t结果");
            for (int i = 0; i < hits.length; ++i) {
                ScoreDoc scoreDoc= hits[i];
                int docId = scoreDoc.doc;
                Document d = searcher.doc(docId);
                List<IndexableField> fields= d.getFields();
                System.out.print((i + 1) );
                System.out.print("\t" + scoreDoc.score);
                for (IndexableField f : fields) {
     
                    if("name".equals(f.name())){
                        TokenStream tokenStream = analyzer.tokenStream(f.name(), new StringReader(d.get(f.name())));
                        String fieldContent = highlighter.getBestFragment(tokenStream, d.get(f.name()));
                        System.out.print("\t"+fieldContent);
                    }
                    else{
                        System.out.print("\t"+d.get(f.name()));
                    }
                }
                System.out.println("<br>");
            }
        }
     
        private static Directory createIndex(IKAnalyzer analyzer) throws IOException {
            Directory index = new RAMDirectory();
            IndexWriterConfig config = new IndexWriterConfig(analyzer);
            IndexWriter writer = new IndexWriter(index, config);
            String fileName = "140k_products.txt";
            List<Product> products = ProductUtil.file2list(fileName);
            int total = products.size();
            int count = 0;
            int per = 0;
            int oldPer =0;
            for (Product p : products) {
                addDoc(writer, p);
                count++;
                per = count*100/total;
                if(per!=oldPer){
                    oldPer = per;
                    System.out.printf("索引中,总共要添加 %d 条记录,当前添加进度是: %d%% %n",total,per);
                }
                 
                if(per>10)
                    break;
                 
            }
            writer.close();
            return index;
        }
     
        private static void addDoc(IndexWriter w, Product p) throws IOException {
            Document doc = new Document();
            doc.add(new TextField("id", String.valueOf(p.getId()), Field.Store.YES));
            doc.add(new TextField("name", p.getName(), Field.Store.YES));
            doc.add(new TextField("category", p.getCategory(), Field.Store.YES));
            doc.add(new TextField("price", String.valueOf(p.getPrice()), Field.Store.YES));
            doc.add(new TextField("place", p.getPlace(), Field.Store.YES));
            doc.add(new TextField("code", p.getCode(), Field.Store.YES));
            w.addDocument(doc);
        }
    }




















lucene搜索分页过程中,可以有两种方式 一种是将搜索结果集直接放到session中,但是假如结果集非常大,同时又存在大并发访问的时候,很可能造成服务器的内存不足,而使服务器宕机 还有一种是每次都重新进行搜索,这样虽然避免了内存溢出的可能,但是,每次搜索都要进行一次IO操作,如果大并发访问的时候,你要保证你的硬盘的转速足够的快,还要保证你的cpu有足够高的频率 而我们可以将这两种方式结合下,每次查询都多缓存一部分的结果集,翻页的时候看看所查询的内容是不是在已经存在在缓存当中,如果已经存在了就直接拿出来,如果不存在,就进行查询后,从缓存中读出来. 比如:现在我们有一个搜索结果集 一个有100条数据,每页显示10条,就有10页数据. 安装第一种的思路就是,我直接把这100条数据缓存起来,每次翻页时从缓存种读取 而第二种思路就是,我直接从搜索到的结果集种显示前十条给第一页显示,第二页的时候,我在查询一次,给出10-20条数据给第二页显示,我每次翻页都要重新查询 第三种思路就变成了 我第一页仅需要10条数据,但是我一次读出来50条数据,把这50条数据放入到缓存当中,当我需要10--20之间的数据的时候,我的发现我的这些数据已经在我的缓存种存在了,我就直接存缓存中把数据读出来,少了一次查询,速度自然也提高了很多. 如果我访问第六页的数据,我就把我的缓存更新一次.这样连续翻页10次才进行两次IO操作 同时又保证了内存不容易被溢出.而具体缓存设置多少,要看你的服务器的能力和访问的人数来决定
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值