Apache Lucene介紹與使用

Lucene介紹

lucene官方使用手冊: https://lucene.apache.org/core/8_5_0/index.html

1. Lucene簡介

  最受歡迎的java開源全文搜索引擎開發工具包。提供了完整的查詢引擎和索引引擎,部分文本分詞引擎(英文與德文兩種西方語言)。Lucene的目的是爲軟件開發人員提供一個簡單易用的工具包,以方便在目標系統中實現全文檢索功能,或者是以此爲基礎建立起完整的全文檢索引擎。是Apache的子項目,網址:http://lucene.apache.org/

2. Lucene用途

  爲軟件開發人員提供一個簡單易用的工具包,以方便在目標系統中實現全文檢索功能,或者是以此爲基礎建立起完整的全文檢索引擎。

3. Lucene適用場景

  在應用中爲數據庫中的數據提供全文檢索實現。

  開發獨立的搜索引擎服務、系統

4. Lucene的特性

  1、穩定、索引性能高

    每小時能夠索引150GB以上的數據。

    對內存的要求小——只需要1MB的堆內存

    增量索引和批量索引一樣快。

     索引的大小約爲索引文本大小的20%~30%。

  2、高效、準確、高性能的搜索算法

    良好的搜索排序。

    強大的查詢方式支持:短語查詢、通配符查詢、臨近查詢、範圍查詢等。

    支持字段搜索(如標題、作者、內容)。

    可根據任意字段排序

    支持多個索引查詢結果合併

    支持更新操作和查詢操作同時進行

    支持高亮、join、分組結果功能

    速度快

    可擴展排序模塊,內置包含向量空間模型、BM25模型可選

    可配置存儲引擎

  3、跨平臺

    純java編寫。

    作爲Apache開源許可下的開源項目,你可在商業或開源項目中使用。

     Lucene有多種語言實現版可選(如C、C++、Python等),不光是JAVA。

 

Lucene使用

1. 先引入依賴lucene依賴

<properties>
    <lunece.version>4.10.2</lunece.version>

</properties>
<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <!-- lucene核心庫 -->
    <dependency>
        <groupId>org.apache.lucene</groupId>
        <artifactId>lucene-core</artifactId>
        <version>${lunece.version}</version>
    </dependency>
    <!-- Lucene的查詢解析器 -->
    <dependency>
        <groupId>org.apache.lucene</groupId>
        <artifactId>lucene-queryparser</artifactId>
        <version>${lunece.version}</version>
    </dependency>
    <!-- lucene的默認分詞器庫 -->
    <dependency>
        <groupId>org.apache.lucene</groupId>
        <artifactId>lucene-analyzers-common</artifactId>
        <version>${lunece.version}</version>
    </dependency>
    <!-- lucene的高亮顯示 -->
    <dependency>
        <groupId>org.apache.lucene</groupId>
        <artifactId>lucene-highlighter</artifactId>
        <version>${lunece.version}</version>
    </dependency>
</dependencies>

它的核心包結構

 

創建索引

 // 創建索引
    @Test
    public void testCreate() throws Exception{
        //1 創建文檔對象
        Document document = new Document();
        // 創建並添加字段信息。參數:字段的名稱、字段的值、是否存儲,這裏選Store.YES代表存儲到文檔列表。Store.NO代表不存儲
        document.add(new StringField("id", "1", Field.Store.YES));
        // 這裏我們title字段需要用TextField,即創建索引又會被分詞。StringField會創建索引,但是不會被分詞
        document.add(new TextField("title", "谷歌地圖之父跳槽facebook", Field.Store.YES));

        //2 索引目錄類,指定索引在硬盤中的位置
        Directory directory = FSDirectory.open(new File("d:\\indexDir"));
        //3 創建分詞器對象
        Analyzer analyzer = new StandardAnalyzer();
        //4 索引寫出工具的配置對象
        IndexWriterConfig conf = new IndexWriterConfig(Version.LATEST, analyzer);
        //5 創建索引的寫出工具類。參數:索引的目錄和配置信息
        IndexWriter indexWriter = new IndexWriter(directory, conf);

        //6 把文檔交給IndexWriter
        indexWriter.addDocument(document);
        //7 提交
        indexWriter.commit();
        //8 關閉
        indexWriter.close();
 }

批量創建索引

// 批量創建索引
    @Test
    public void testCreate2() throws Exception{
        // 創建文檔的集合
        Collection<Document> docs = new ArrayList<>();
        // 創建文檔對象
        Document document1 = new Document();
        document1.add(new StringField("id", "1", Field.Store.YES));
        document1.add(new TextField("title", "谷歌地圖之父跳槽facebook", Field.Store.YES));
        docs.add(document1);
        // 創建文檔對象
        Document document2 = new Document();
        document2.add(new StringField("id", "2", Field.Store.YES));
        document2.add(new TextField("title", "谷歌地圖之父加盟FaceBook", Field.Store.YES));
        docs.add(document2);
        // 創建文檔對象
        Document document3 = new Document();
        document3.add(new StringField("id", "3", Field.Store.YES));
        document3.add(new TextField("title", "谷歌地圖創始人拉斯離開谷歌加盟Facebook", Field.Store.YES));
        docs.add(document3);
        // 創建文檔對象
        Document document4 = new Document();
        document4.add(new StringField("id", "4", Field.Store.YES));
        document4.add(new TextField("title", "谷歌地圖之父跳槽Facebook與Wave項目取消有關", Field.Store.YES));
        docs.add(document4);
        // 創建文檔對象
        Document document5 = new Document();
        document5.add(new StringField("id", "5", Field.Store.YES));
        document5.add(new TextField("title", "谷歌地圖之父拉斯加盟社交網站Facebook", Field.Store.YES));
        docs.add(document5);

        // 索引目錄類,指定索引在硬盤中的位置
        Directory directory = FSDirectory.open(new File("d:\\indexDir"));
        // 引入IK分詞器
        Analyzer analyzer = new IKAnalyzer();
        // 索引寫出工具的配置對象
        IndexWriterConfig conf = new IndexWriterConfig(Version.LATEST, analyzer);
        // 設置打開方式:OpenMode.APPEND 會在索引庫的基礎上追加新索引。OpenMode.CREATE會先清空原來數據,再提交新的索引
        conf.setOpenMode(IndexWriterConfig.OpenMode.CREATE);

        // 創建索引的寫出工具類。參數:索引的目錄和配置信息
        IndexWriter indexWriter = new IndexWriter(directory, conf);
        // 把文檔集合交給IndexWriter
        indexWriter.addDocuments(docs);
        // 提交
        indexWriter.commit();
        // 關閉
        indexWriter.close();
    }

查詢索引數據

@Test
    public void testSearch() throws Exception {
        // 索引目錄對象
        Directory directory = FSDirectory.open(new File("d:\\indexDir"));
        // 索引讀取工具
        IndexReader reader = DirectoryReader.open(directory);
        // 索引搜索工具
        IndexSearcher searcher = new IndexSearcher(reader);

        // 創建查詢解析器,兩個參數:默認要查詢的字段的名稱,分詞器
        QueryParser parser = new QueryParser("title", new IKAnalyzer());
        // 創建查詢對象
        Query query = parser.parse("谷歌");

        // 搜索數據,兩個參數:查詢條件對象要查詢的最大結果條數
        // 返回的結果是 按照匹配度排名得分前N名的文檔信息(包含查詢到的總條數信息、所有符合條件的文檔的編號信息)。
        TopDocs topDocs = searcher.search(query, 10);
        // 獲取總條數
        System.out.println("本次搜索共找到" + topDocs.totalHits + "條數據");
        // 獲取得分文檔對象(ScoreDoc)數組.SocreDoc中包含:文檔的編號、文檔的得分
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        for (ScoreDoc scoreDoc : scoreDocs) {
            // 取出文檔編號
            int docID = scoreDoc.doc;
            // 根據編號去找文檔
            Document doc = reader.document(docID);
            System.out.println("id: " + doc.get("id"));
            System.out.println("title: " + doc.get("title"));
            // 取出文檔得分
            System.out.println("得分: " + scoreDoc.score);
        }
    }

特殊查詢

public void search(Query query) throws Exception {
        // 索引目錄對象
        Directory directory = FSDirectory.open(new File("indexDir"));
        // 索引讀取工具
        IndexReader reader = DirectoryReader.open(directory);
        // 索引搜索工具
        IndexSearcher searcher = new IndexSearcher(reader);

        // 搜索數據,兩個參數:查詢條件對象要查詢的最大結果條數
        // 返回的結果是 按照匹配度排名得分前N名的文檔信息(包含查詢到的總條數信息、所有符合條件的文檔的編號信息)。
        TopDocs topDocs = searcher.search(query, 10);
        // 獲取總條數
        System.out.println("本次搜索共找到" + topDocs.totalHits + "條數據");
        // 獲取得分文檔對象(ScoreDoc)數組.SocreDoc中包含:文檔的編號、文檔的得分
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;

        for (ScoreDoc scoreDoc : scoreDocs) {
            // 取出文檔編號
            int docID = scoreDoc.doc;
            // 根據編號去找文檔
            Document doc = reader.document(docID);
            System.out.println("id: " + doc.get("id"));
            System.out.println("title: " + doc.get("title"));
            // 取出文檔得分
            System.out.println("得分: " + scoreDoc.score);
        }
    }

詞條查詢

/*
     * 測試普通詞條查詢
     * 注意:Term(詞條)是搜索的最小單位,不可再分詞。值必須是字符串!
     */
    @Test
    public void testTermQuery() throws Exception {
        // 創建詞條查詢對象
        Query query = new TermQuery(new Term("title", "谷歌地圖"));
        search(query);
    }

通配符查詢

 /*
     * 測試通配符查詢
     * 	? 可以代表任意一個字符
     * 	* 可以任意多個任意字符
     */
    @Test
    public void testWildCardQuery() throws Exception {
        // 創建查詢對象
        Query query = new WildcardQuery(new Term("title", "*歌*"));
        search(query);
    }

模糊查詢

 /*
     * 測試通配符查詢
     * 	? 可以代表任意一個字符
     * 	* 可以任意多個任意字符
     */
    @Test
    public void testWildCardQuery() throws Exception {
        // 創建查詢對象
        Query query = new WildcardQuery(new Term("title", "*歌*"));
        search(query);
    }

數值範圍查詢

/*
	 * 測試:數值範圍查詢
	 * 注意:數值範圍查詢,可以用來對非String類型的ID進行精確的查找
	 */
	@Test
	public void testNumericRangeQuery() throws Exception{
		// 數值範圍查詢對象,參數:字段名稱,最小值、最大值、是否包含最小值、是否包含最大值
		Query query = NumericRangeQuery.newLongRange("id", 2L, 2L, true, true);
		
		search(query);
	}

組合查詢

 /*
     * 布爾查詢:
     * 	布爾查詢本身沒有查詢條件,可以把其它查詢通過邏輯運算進行組合!
     * 交集:Occur.MUST + Occur.MUST
     * 並集:Occur.SHOULD + Occur.SHOULD
     * 非:Occur.MUST_NOT
     */
    @Test
    public void testBooleanQuery() throws Exception{

        Query query1 = NumericRangeQuery.newLongRange("id", 1L, 3L, true, true);
        Query query2 = NumericRangeQuery.newLongRange("id", 2L, 4L, true, true);
        // 創建布爾查詢的對象
        BooleanQuery query = new BooleanQuery();
        // 組合其它查詢
        query.add(query1, BooleanClause.Occur.MUST_NOT);
        query.add(query2, BooleanClause.Occur.SHOULD);

        search(query);
    }

修改索引

/* 測試:修改索引
     * 注意:
     * 	A:Lucene修改功能底層會先刪除,再把新的文檔添加。
     * 	B:修改功能會根據Term進行匹配,所有匹配到的都會被刪除。這樣不好
     * 	C:因此,一般我們修改時,都會根據一個唯一不重複字段進行匹配修改。例如ID
     * 	D:但是詞條搜索,要求ID必須是字符串。如果不是,這個方法就不能用。
     * 如果ID是數值類型,我們不能直接去修改。可以先手動刪除deleteDocuments(數值範圍查詢鎖定ID),再添加。
     */
@Test
public void testUpdate() throws Exception{
    // 創建目錄對象
    Directory directory = FSDirectory.open(new File("indexDir"));
    // 創建配置對象
    IndexWriterConfig conf = new IndexWriterConfig(Version.LATEST, new IKAnalyzer());
    // 創建索引寫出工具
    IndexWriter writer = new IndexWriter(directory, conf);

    // 創建新的文檔數據
    Document doc = new Document();
    doc.add(new StringField("id","1",Store.YES));
    doc.add(new TextField("title","谷歌地圖之父跳槽facebook ",Store.YES));
    /* 修改索引。參數:
         * 	詞條:根據這個詞條匹配到的所有文檔都會被修改
         * 	文檔信息:要修改的新的文檔數據
         */
    writer.updateDocument(new Term("id","1"), doc);
    // 提交
    writer.commit();
    // 關閉
    writer.close();
}

刪除索引

/*
     * 演示:刪除索引
     * 注意:
     * 	一般,爲了進行精確刪除,我們會根據唯一字段來刪除。比如ID
     * 	如果是用Term刪除,要求ID也必須是字符串類型!
     */
@Test
public void testDelete() throws Exception {
    // 創建目錄對象
    Directory directory = FSDirectory.open(new File("indexDir"));
    // 創建配置對象
    IndexWriterConfig conf = new IndexWriterConfig(Version.LATEST, new IKAnalyzer());
    // 創建索引寫出工具
    IndexWriter writer = new IndexWriter(directory, conf);

    // 根據詞條進行刪除
    //		writer.deleteDocuments(new Term("id", "1"));

    // 根據query對象刪除,如果ID是數值類型,那麼我們可以用數值範圍查詢鎖定一個具體的ID
    //		Query query = NumericRangeQuery.newLongRange("id", 2L, 2L, true, true);
    //		writer.deleteDocuments(query);

    // 刪除所有
    writer.deleteAll();
    // 提交
    writer.commit();
    // 關閉
    writer.close();
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章