1. 程式人生 > >Lucene之分頁查詢的三種方式-yellowcong

Lucene之分頁查詢的三種方式-yellowcong

分頁查詢有三種,一種是直接查詢出這頁及這頁以後的資料,第二種,查詢這頁以前的最後一條資料,然後再查詢這頁之後的資料,這種方式還不如第一種方法快,第三種,是根據一個id來進行分頁,這種方式適合不變更的資料

方法1

思路是將所有的查詢取來,然後取自己當前頁需要的資料

/**
     * 建立使用者:狂飆的yellowcong<br/>
     * 建立日期:2017年12月3日<br/>
     * 建立時間:下午3:37:17<br/>
     * 機能概要: 查詢所有資料後再進行分頁
     */
    public static void queryByPager1
(int pageNow,int pageSize){ IndexReader reader = null; try { reader = getIndexReader(); QueryParser parser = new QueryParser(Version.LUCENE_45, "content",new StandardAnalyzer(Version.LUCENE_45)); Query query = parser.parse("username:yellow*"); //建立IndexSearcher
IndexSearcher searcher = new IndexSearcher(reader); //查詢資料, 結束頁面自前的資料都會查詢到,但是隻取本頁的資料 TopDocs topDocs = searcher.search(query, pageSize*pageNow); System.out.println("查詢到的條數\t"+topDocs.totalHits); ScoreDoc [] scores = topDocs.scoreDocs; int
start = (pageNow -1)*pageSize ; int end = pageSize*pageNow; for(int i=start;i<end;i++){ Document doc = reader.document(scores[i].doc); System.out.println(doc.get("id")+":"+doc.get("username")+":"+doc.get("email")); } } catch (Exception e) { e.printStackTrace(); }finally{ coloseReader(reader); } }

方法2

第二個方法,就是先獲取前一條的記錄(ScoreDoc),然後IndexSearcher.searchAfter再獲取後面的記錄

/**
     * 建立使用者:狂飆的yellowcong<br/>
     * 建立日期:2017年12月3日<br/>
     * 建立時間:下午3:54:55<br/>
     * 機能概要:通過IndexSearcher.searchAfter
     * 
     * @param pageNow
     * @param pageSize
     */
    public static void queryByPager2(int pageNow, int pageSize) {
        IndexReader reader = null;
        try {
            reader = getIndexReader();

            QueryParser parser = new QueryParser(Version.LUCENE_45, "content", new StandardAnalyzer(Version.LUCENE_45));

            Query query = parser.parse("username:yellow*");

            // 建立IndexSearcher
            IndexSearcher searcher = new IndexSearcher(reader);

            int start = (pageNow - 1) * pageSize;
            // 查詢資料, 結束頁面自前的資料都會查詢到,但是隻取本頁的資料
            TopDocs topDocs = searcher.search(query, start);
            //獲取到上一頁最後一條
            ScoreDoc preScore = topDocs.scoreDocs[start-1];

            //查詢最後一條後的資料的一頁資料
            topDocs = searcher.searchAfter(preScore, query, pageSize);
            ScoreDoc[] scores = topDocs.scoreDocs;

            System.out.println("查詢到的條數\t" + topDocs.totalHits);
            //讀取資料
            for (int i = 0; i < scores.length; i++) {
                Document doc = reader.document(scores[i].doc);
                System.out.println(doc.get("id") + ":" + doc.get("username") + ":" + doc.get("email"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            coloseReader(reader);
        }
    }

方法3

通過Id的方式來解決這個問題,但是id這種查詢方式需要注意的是隻能對不變化的資料,通過id分頁,但是不能對其他的條件進行分頁

/**
     * 建立使用者:狂飆的yellowcong<br/>
     * 建立日期:2017年12月3日<br/>
     * 建立時間:下午2:38:27<br/>
     * 機能概要:分頁查詢
     * 
     * @param pageSize
     * @param pageNow
     * @throws Exception
     */
    public static void queryByPager3(int pageNow, int pageSize) {
        IndexReader reader = null;
        try {
            reader = getIndexReader();

            Query query = NumericRangeQuery.newIntRange("id", (pageNow - 1) * pageSize + 1, pageNow * pageSize, true,
                    true);

            // 建立IndexSearcher
            IndexSearcher searcher = new IndexSearcher(reader);

            // 查詢資料
            TopDocs topDocs = searcher.search(query, pageSize);

            for (ScoreDoc score : topDocs.scoreDocs) {
                Document doc = reader.document(score.doc);
                System.out.println(doc.get("id") + ":" + doc.get("username") + ":" + doc.get("email"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            coloseReader(reader);
        }
    }

完整程式碼

package com.yellowcong.demo;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.IntField;
import org.apache.lucene.document.LongField;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.FuzzyQuery;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.NumericRangeQuery;
import org.apache.lucene.search.PhraseQuery;
import org.apache.lucene.search.PrefixQuery;
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.search.WildcardQuery;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;

/**
 * 建立使用者:狂飆的yellowcong<br/>
 * 建立時間:下午5:27:50<br/>
 * 建立日期:2017年12月2日<br/>
 * 機能概要:
 */
public class Demo6 {
    private static List<Passage> psgList = null;

    // 寫物件
    private static IndexWriter writer = null;

    public static void main(String[] args) throws Exception {

        // 刪除 所有索引
        deleteAll();

        // 建立索引
        index();

        queryByPager3(3, 10);
    }

    /**
     * 建立使用者:狂飆的yellowcong<br/>
     * 建立日期:2017年12月3日<br/>
     * 建立時間:下午3:54:55<br/>
     * 機能概要:通過IndexSearcher.searchAfter
     * 
     * @param pageNow
     * @param pageSize
     */
    public static void queryByPager2(int pageNow, int pageSize) {
        IndexReader reader = null;
        try {
            reader = getIndexReader();

            QueryParser parser = new QueryParser(Version.LUCENE_45, "content", new StandardAnalyzer(Version.LUCENE_45));

            Query query = parser.parse("username:yellow*");

            // 建立IndexSearcher
            IndexSearcher searcher = new IndexSearcher(reader);

            int start = (pageNow - 1) * pageSize;
            // 查詢資料, 結束頁面自前的資料都會查詢到,但是隻取本頁的資料
            TopDocs topDocs = searcher.search(query, start);
            //獲取到上一頁最後一條
            ScoreDoc preScore = topDocs.scoreDocs[start-1];

            //查詢最後一條後的資料的一頁資料
            topDocs = searcher.searchAfter(preScore, query, pageSize);
            ScoreDoc[] scores = topDocs.scoreDocs;

            System.out.println("查詢到的條數\t" + topDocs.totalHits);
            //讀取資料
            for (int i = 0; i < scores.length; i++) {
                Document doc = reader.document(scores[i].doc);
                System.out.println(doc.get("id") + ":" + doc.get("username") + ":" + doc.get("email"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            coloseReader(reader);
        }
    }

    /**
     * 建立使用者:狂飆的yellowcong<br/>
     * 建立日期:2017年12月3日<br/>
     * 建立時間:下午3:37:17<br/>
     * 機能概要: 查詢所有資料後再進行分頁
     */
    public static void queryByPager1(int pageNow, int pageSize) {
        IndexReader reader = null;
        try {
            reader = getIndexReader();

            QueryParser parser = new QueryParser(Version.LUCENE_45, "content", new StandardAnalyzer(Version.LUCENE_45));

            Query query = parser.parse("username:yellow*");

            // 建立IndexSearcher
            IndexSearcher searcher = new IndexSearcher(reader);

            // 查詢資料, 結束頁面自前的資料都會查詢到,但是隻取本頁的資料
            TopDocs topDocs = searcher.search(query, pageSize * pageNow);

            // searcher.searchAfter(after, query, n)
            System.out.println("查詢到的條數\t" + topDocs.totalHits);
            ScoreDoc[] scores = topDocs.scoreDocs;

            int start = (pageNow - 1) * pageSize;
            int end = pageSize * pageNow;
            for (int i = start; i < end; i++) {
                Document doc = reader.document(scores[i].doc);
                System.out.println(doc.get("id") + ":" + doc.get("username") + ":" + doc.get("email"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            coloseReader(reader);
        }
    }

    /**
     * 建立使用者:狂飆的yellowcong<br/>
     * 建立日期:2017年12月3日<br/>
     * 建立時間:下午2:38:27<br/>
     * 機能概要:分頁查詢
     * 
     * @param pageSize
     * @param pageNow
     * @throws Exception
     */
    public static void queryByPager3(int pageNow, int pageSize) {
        IndexReader reader = null;
        try {
            reader = getIndexReader();

            Query query = NumericRangeQuery.newIntRange("id", (pageNow - 1) * pageSize + 1, pageNow * pageSize, true,
                    true);

            // 建立IndexSearcher
            IndexSearcher searcher = new IndexSearcher(reader);

            // 查詢資料
            TopDocs topDocs = searcher.search(query, pageSize);

            for (ScoreDoc score : topDocs.scoreDocs) {
                Document doc = reader.document(score.doc);
                System.out.println(doc.get("id") + ":" + doc.get("username") + ":" + doc.get("email"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            coloseReader(reader);
        }
    }

    /**
     * 建立使用者:狂飆的yellowcong<br/>
     * 建立日期:2017年12月3日<br/>
     * 建立時間:下午12:01:55<br/>
     * 機能概要:查詢Query 將需要查詢的條件傳遞進來
     * 
     * @param query
     */
    public static void excQuery(Query query) {
        // 查詢
        IndexReader reader = null;
        try {
            reader = getIndexReader();

            // 獲取查詢資料
            IndexSearcher searcher = new IndexSearcher(reader);

            // 檢索資料
            TopDocs topDocs = searcher.search(query, 100);
            for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
                // 湖區偶
                Document doc = reader.document(scoreDoc.doc);
                System.out.println(doc.get("id") + ":" + doc.get("username") + ":" + doc.get("email"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            coloseReader(reader);
        }
    }

    /**
     * 建立使用者:狂飆的yellowcong<br/>
     * 建立日期:2017年12月3日<br/>
     * 建立時間:上午11:52:52<br/>
     * 機能概要:關閉IndexReader
     * 
     * @param reader
     */
    private static void coloseReader(IndexReader reader) {
        try {
            if (reader != null) {
                reader.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    static {
        psgList = new ArrayList<Passage>();

        // 產生一堆資料
        for (int i = 1; i <= 200; i++) {
            psgList.add(new Passage(i, "yellowcong", "[email protected]", "逗比", 23, "I LOVE YOU ", new Date()));
        }
    }

    /**
     * 建立使用者:狂飆的yellowcong<br/>
     * 建立日期:2017年12月2日<br/>
     * 建立時間:下午5:43:57<br/>
     * 機能概要:獲取IndexWriter 同一時間 ,只能開啟一個 IndexWriter,獨佔寫鎖 。內建執行緒安全機制。
     * 
     * @return
     * @throws Exception
     */
    @SuppressWarnings("static-access")
    public static IndexWriter getIndexWriter() throws Exception {
        // 建立IdnexWriter
        String path = getIndexPath();
        FSDirectory fs = FSDirectory.open(new File(path));
        // 判斷資源是否佔用
        if (writer == null || !writer.isLocked(fs)) {
            synchronized (Demo3.class) {
                if (writer == null || !writer.isLocked(fs)) {
                    // 建立writer物件
                    writer = new IndexWriter(fs,
                            new IndexWriterConfig(Version.LUCENE_45, new StandardAnalyzer(Version.LUCENE_45)));
                }
            }
        }
        return writer;
    }

    /**
     * 建立使用者:狂飆的yellowcong<br/>
     * 建立日期:2017年12月2日<br/>
     * 建立時間:下午5:46:36<br/>
     * 機能概要:獲取到IndexReader 任意多個IndexReaders可同時開啟,可以跨JVM。
     * 
     * @return
     * @throws Exception
     */
    public static IndexReader getIndexReader() throws Exception {
        // 建立IdnexWriter
        String path = getIndexPath();
        FSDirectory fs = FSDirectory.open(new File(path));
        // 獲取到讀
        return IndexReader.open(fs);
    }

    /**
     * 建立使用者:狂飆的yellowcong<br/>
     * 建立日期:2017年12月2日<br/>
     * 建立時間:下午7:57:04<br/>
     * 機能概要:刪除所有的索引
     */
    public static void deleteAll() {
        IndexWriter writer = null;
        try {
            // 獲取IndexWriter
            writer = getIndexWriter();

            // 刪除所有的資料
            writer.deleteAll();

            int cnt = writer.numDocs();
            System.out.println("索引條數\t" + cnt);

            // 提交事物
            writer.commit();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            coloseWriter(writer);
        }
    }

    /**
     * 建立使用者:狂飆的yellowcong<br/>
     * 建立日期:2017年12月2日<br/>
     * 建立時間:下午5:37:22<br/>
     * 機能概要:獲取索引目錄
     * 
     * @return 目錄
     */
    private static String getIndexPath() {
        // 獲取索引的目錄
        String path = Demo3.class.getClassLoader().getResource("index").getPath();

        // 不存在就建立目錄
        File file = new File(path);
        if (!file.exists()) {
            file.mkdirs();
        }
        return path;
    }

    /**
     * 建立使用者:狂飆的yellowcong<br/>
     * 建立日期:2017年12月2日<br/>
     * 建立時間:下午8:24:16<br/>
     * 機能概要:關閉IndexWriter
     */
    private static void coloseWriter(IndexWriter writer) {
        try {
            if (writer != null) {
                writer.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 建立使用者:狂飆的yellowcong<br/>
     * 建立日期:2017年12月2日<br/>
     * 建立時間:下午8:26:15<br/>
     * 機能概要:關閉IndexReader
     * 
     * @param reader
     */
    public static void closerReader(IndexReader reader) {
        try {
            if (reader != null) {
                reader.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 建立使用者:狂飆的yellowcong<br/>
     * 建立日期:2017年12月2日<br/>
     * 建立時間:下午8:10:01<br/>
     * 機能概要: 查詢資料
     * 
     * @param key
     *            查詢範圍
     * @param val
     *            值
     */
    public static void search(String key, Object val) {
        IndexReader reader = null;
        try {
            reader = getIndexReader();

            IndexSearcher searcher = new IndexSearcher(reader);

            // 精確查詢
            Query query = null;

            // 定義查詢條件
            if (val instanceof Integer) {
                // 後面的兩個true 表示的是 是否包含 上下的資料
                query = NumericRangeQuery.newIntRange(key, Integer.parseInt(val.toString()),
                        Integer.parseInt(val.toString()), true, true);
            } else {
                query = new TermQuery(new Term(key, val.toString()));
            }
            // QueryParser paraser = new QueryParser(Version.LUCENE_45, key, new
            // StandardAnalyzer(Version.LUCENE_45));
            // Query query = paraser.parse(val);

            // 獲取查詢到的Docuemnt
            TopDocs topDocs = searcher.search(query, 500);
            // 總共命中的條數
            System.out.println(topDocs.totalHits);
            for (ScoreDoc score : topDocs.scoreDocs) {
                //
                Document doc = searcher.doc(score.doc);

                // 查詢到的結果
                String username = doc.get("username");
                System.out.println(username);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            closerReader(reader);
        }
    }

    /**
     * 建立使用者:狂飆的yellowcong<br/>
     * 建立日期:2017年12月2日<br/>
     * 建立時間:下午6:03:33<br/>
     * 機能概要:建立索引
     */
    public static void index() {
        IndexWriter writer = null;
        try {
            // 1、獲取IndexWriter
            writer = getIndexWriter();

            // 2、建立索引
            for (Passage psg : psgList) {
                Document doc = new Document();

                // IntField 不能直接檢索到,需要結合
                doc.add(new IntField("id", psg.getId(), Field.Store.YES));

                // 使用者String型別的欄位的儲存,StringField是隻索引不分詞
                doc.add(new TextField("username", psg.getUsername(), Field.Store.YES));

                // 主要對int型別的欄位進行儲存,需要注意的是如果需要對InfField進行排序使用SortField.Type.INT來比較,如果進範圍查詢或過濾,需要採用NumericRangeQuery.newIntRange()
                doc.add(new IntField("age", psg.getAge(), Field.Store.YES));

                // 對String型別的欄位進行儲存,TextField和StringField的不同是TextField既索引又分詞
                doc.add(new TextField("content", psg.getContent(), Field.Store.NO));

                doc.add(new StringField("keyword", psg.getKeyword(), Field.Store.YES));

                doc.add(new StringField("email", psg.getEmail(), Field.Store.YES));

                // 日期資料新增索引
                doc.add(new LongField("addDate", psg.getAddDate().getTime(), Field.Store.YES));

                // 3、新增文件
                writer.addDocument(doc);
            }

            // 索引條數
            int cnt = writer.numDocs();
            System.out.println("索引條數\t" + cnt);

            // 提交事物
            writer.commit();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            coloseWriter(writer);
        }
    }

    static class Passage {
        private int id;
        private String username;
        private String email;
        private String keyword;
        private int age;
        // 這個模擬的是文章
        private String content;
        private Date addDate;

        public Passage(int id, String username, String email, String keyword, int age, String content, Date addDate) {
            super();
            this.id = id;
            this.username = username;
            this.email = email;
            this.keyword = keyword;
            this.age = age;
            this.content = content;
            this.addDate = addDate;
        }

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public String getUsername() {
            return username;
        }

        public void setUsername(String username) {
            this.username = username;
        }

        public String getEmail() {
            return email;
        }

        public void setEmail(String email) {
            this.email = email;
        }

        public String getKeyword() {
            return keyword;
        }

        public void setKeyword(String keyword) {
            this.keyword = keyword;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        public String getContent() {
            return content;
        }

        public void setContent(String content) {
            this.content = content;
        }

        public Date getAddDate() {
            return addDate;
        }

        public void setAddDate(Date addDate) {
            this.addDate = addDate;
        }
    }
}