欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Lucene 全文检索

程序员文章站 2022-07-05 08:30:08
基于 lucene 8 1 Lucene简介 Lucene是apache下的一个开源的全文检索引擎工具包。 1.1 全文检索(Full text Search) 全文检索就是先分词创建索引,再执行搜索的过程。分词就是将一段文字分成一个个单词。全文检索就将一段文字分成一个个单词去查询数据 1.2 Lu ......

基于 lucene 8

1 lucene简介

lucene是apache下的一个开源的全文检索引擎工具包。

全文检索就是先分词创建索引,再执行搜索的过程。分词就是将一段文字分成一个个单词。全文检索就将一段文字分成一个个单词去查询数据

1.2 lucene实现全文检索的流程

Lucene 全文检索

全文检索的流程分为两大部分:索引流程、搜索流程。

  • 索引流程:采集数据--->构建文档对象--->创建索引(将文档写入索引库)。
  • 搜索流程:创建查询--->执行搜索--->渲染搜索结果。

2 入门示例

2.1 需求

使用lucene实现电商项目中图书类商品的索引和搜索功能。

2.2 配置步骤说明

  1. 搭建环境
  2. 创建索引库
  3. 搜索索引库

2.3 配置步骤

2.3.1 第一部分:搭建环境(创建项目,导入包)

Lucene 全文检索

2.3.2 第二部分:创建索引

步骤说明:

  1. 采集数据
  2. 将数据转换成lucene文档
  3. 将文档写入索引库,创建索引

2.3.2.1 第一步:采集数据

lucene全文检索,不是直接查询数据库,所以需要先将数据采集出来。

package jdbc.dao;

import jdbc.pojo.book;
import jdbc.util.jdbcutils;

import java.sql.connection;
import java.sql.preparedstatement;
import java.sql.resultset;
import java.sql.sqlexception;
import java.util.arraylist;
import java.util.list;

public class bookdao {
    public list<book> listall() {
        //创建集合
        list<book> books = new arraylist<>();

        //获取数据库连接
        connection conn = jdbcutils.getconnection();

        string sql = "select * from `book`";
        preparedstatement preparedstatement = null;
        resultset resultset = null;
        try {
            //获取预编译语句
            preparedstatement = conn.preparestatement(sql);

            //获取结果集
            resultset = preparedstatement.executequery();

            //结果集解析
            while (resultset.next()) {
                books.add(new book(resultset.getint("id"),
                        resultset.getstring("name"),
                        resultset.getfloat("price"),
                        resultset.getstring("pic"),
                        resultset.getstring("description")));
            }
        } catch (sqlexception e) {
            e.printstacktrace();
        } finally {
            //关闭资源
            if (null != resultset) {
                try {
                    resultset.close();
                } catch (sqlexception e) {
                    e.printstacktrace();
                } finally {
                    if (preparedstatement != null) {
                        try {
                            preparedstatement.close();
                        } catch (sqlexception e) {
                            e.printstacktrace();
                        } finally {
                            if (null != conn) {
                                try {
                                    conn.close();
                                } catch (sqlexception e) {
                                    e.printstacktrace();
                                }
                            }
                        }
                    }
                }
            }
        }
        return books;
    }
}

2.3.2.2 第二步:将数据转换成lucene文档

lucene是使用文档类型来封装数据的,所有需要先将采集的数据转换成文档类型。其格式为:

Lucene 全文检索

修改bookdao,新增一个方法,转换数据

public list<document> getdocuments(list<book> books) {
    //创建集合
    list<document> documents = new arraylist<>();
    
    //循环操作 books 集合
    books.foreach(book -> {
        //创建 document 对象,document 内需要设置一个个 field 对象
        document doc = new document();
        //创建各个 field
        field id = new textfield("id", book.getid().tostring(), field.store.yes);
        field name = new textfield("name", book.getname(), field.store.yes);
        field price = new textfield("price", book.getprice().tostring(), field.store.yes);
        field pic = new textfield("id", book.getpic(), field.store.yes);
        field description = new textfield("description", book.getdescription(), field.store.yes);
        //将 field 添加到文档中
        doc.add(id);
        doc.add(name);
        doc.add(price);
        doc.add(pic);
        doc.add(description);
        
        documents.add(doc);
    });
    return documents;
}

2.3.2.3 第三步:创建索引库

lucene是在将文档写入索引库的过程中,自动完成分词、创建索引的。因此创建索引库,从形式上看,就是将文档写入索引库!

package jdbc.test;

import jdbc.dao.bookdao;
import org.apache.lucene.analysis.standard.standardanalyzer;
import org.apache.lucene.index.indexwriter;
import org.apache.lucene.index.indexwriterconfig;
import org.apache.lucene.store.directory;
import org.apache.lucene.store.fsdirectory;
import org.junit.test;

import java.io.file;
import java.io.ioexception;

public class lucenetest {

    /**
     * 创建索引库
     */
    @test
    public void createindex() {
        bookdao dao = new bookdao();
        //该分词器用于逐个字符分词
        standardanalyzer standardanalyzer = new standardanalyzer();
        //创建索引
        //1. 创建索引库存储目录
        try (directory directory = fsdirectory.open(new file("c:\\users\\carlo\\onedrive\\workspace\\ideaprojects\\lucene-demo01-start\\lucene").topath())) {
            //2. 创建 indexwriterconfig 对象
            indexwriterconfig ifc = new indexwriterconfig(standardanalyzer);
            //3. 创建 indexwriter 对象
            indexwriter indexwriter = new indexwriter(directory, ifc);
            //4. 通过 indexwriter 对象添加文档
            indexwriter.adddocuments(dao.getdocuments(dao.listall()));
            //5. 关闭 indexwriter
            indexwriter.close();

            system.out.println("完成索引库创建");
        } catch (ioexception e) {
            e.printstacktrace();
        }
    }
}

可以通过 luke 工具查看结果

Lucene 全文检索

2.3.3 第三部分:搜索索引

2.3.3.1 说明

搜索的时候,需要指定搜索哪一个域(也就是字段),并且,还要对搜索的关键词做分词处理。

2.3.3.2 执行搜索

@test
public void searchtest() {
    //1. 创建查询(query 对象)
    standardanalyzer standardanalyzer = new standardanalyzer();
    // 参数 1 指定搜索的 field
    queryparser queryparser = new queryparser("name", standardanalyzer);
    try {
        query query = queryparser.parse("java book");
        //2. 执行搜索
        //a. 指定索引库目录
        directory directory = fsdirectory.open(new file("c:\\users\\carlo\\onedrive\\workspace\\ideaprojects\\lucene-demo01-start\\lucene").topath());
        //b. 创建 indexreader 对象
        indexreader reader = directoryreader.open(directory);
        //c. 创建 indexsearcher 对象
        indexsearcher searcher = new indexsearcher(reader);
        /**
         * d. 通过 indexsearcher 对象查询索引库,返回 topdocs 对象
         * 参数 1:查询对象(query)
         * 参数 2:前 n 条数据
         */
        topdocs topdocs = searcher.search(query, 10);
        //e. 提取 topdocs 对象中的的查询结果
        scoredoc[] scoredocs = topdocs.scoredocs;

        system.out.println("查询结果个数为:" + topdocs.totalhits);

        //循环输出数据对象
        for (scoredoc scoredoc : scoredocs) {
            //获得文档对象 id
            int docid = scoredoc.doc;
            //通过 id 获得具体对象
            document document = searcher.doc(docid);
            //输出图书的书名
            system.out.println(document.get("name"));
        }

        //关闭 indexreader
        reader.close();
    } catch (parseexception | ioexception e) {
        e.printstacktrace();
    }
}

结果

Lucene 全文检索

3 分词

对lucene分词的过程,我们可以做如下总结:

  1. 分词的时候,是以域为单位的。不同的域,相互独立。同一个域中,拆分出来相同的词,视为同一个词(term)。不同的域中,拆分出来相同的词,不是同一个词。其中,term是lucene最小的语汇单元,不可再细分。
  2. 分词的时候经历了一系列的过滤器。如大小写转换、去除停用词等。

Lucene 全文检索

从上图中,我们发现:

  1. 索引库中有两个区域:索引区、文档区。
  2. 文档区存放的是文档。lucene给每一个文档自动加上一个文档编号docid。
  3. 索引区存放的是索引。注意:
    • 索引是以域为单位的,不同的域,彼此相互独立。
    • 索引是根据分词规则创建出来的,根据索引就能找到对应的文档。

4 field域

我们已经知道,lucene是在写入文档时,完成分词、索引的。那lucene是怎么知道如何分词的呢?lucene是根据文档中的域的属性来确定是否要分词、是否创建索引的。所以,我们必须搞清楚域有哪些属性。

4.1 域的属性

4.1.1 三大属性

4.1.1.1 是否分词(tokenized)

只有设置了分词属性为true,lucene才会对这个域进行分词处理。

在实际的开发中,有一些字段是不需要分词的,比如商品id,商品图片等。而有一些字段是必须分词的,比如商品名称,描述信息等。

4.1.1.2 是否索引(indexed)

只有设置了索引属性为true,lucene才为这个域的term词创建索引。

在实际的开发中,有一些字段是不需要创建索引的,比如商品的图片等。我们只需要对参与搜索的字段做索引处理。

4.1.1.3 是否存储(stored)

只有设置了存储属性为true,在查找的时候,才能从文档中获取这个域的值。

在实际开发中,有一些字段是不需要存储的。比如:商品的描述信息。因为商品描述信息,通常都是大文本数据,读的时候会造成巨大的io开销。而描述信息是不需要经常查询的字段,这样的话就白白浪费了cpu的资源了。因此,像这种不需要经常查询,又是大文本的字段,通常不会存储到索引库。

4.1.2 特点

  1. 三大属性彼此独立。
  2. 通常分词是为了创建索引。
  3. 不存储这个域文本内容,也可以对这个域先分词、创建索引。

4.2 field常用类型

域的常用类型有很多,每一个类都有自己默认的三大属性。如下:

Lucene 全文检索

4.3 改造入门示例中的域类型

public list<document> getdocuments(list<book> books) {
    //创建集合
    list<document> documents = new arraylist<>();

    //循环操作 books 集合
    books.foreach(book -> {
        //创建 document 对象,document 内需要设置一个个 field 对象
        document doc = new document();
        //创建各个 field
        //存储但不分词、不索引
        field id = new storedfield("id", book.getid());
        //存储、分词、索引
        field name = new textfield("name", book.getname(), field.store.yes);
        //存储但不分词、不索引
        field price = new storedfield("price", book.getprice());
        //存储但不分词、不索引
        field pic = new storedfield("pic", book.getpic());
        //分词、索引,但不存储
        field description = new textfield("description", book.getdescription(), field.store.no);
        //将 field 添加到文档中
        doc.add(id);
        doc.add(name);
        doc.add(price);
        doc.add(pic);
        doc.add(description);

        documents.add(doc);
    });
    return documents;
}

结果

Lucene 全文检索

Lucene 全文检索

5 索引库维护]

5.1 添加索引(文档)

5.1.1 需求

数据库中新上架了图书,必须把这些图书也添加到索引库中,不然就搜不到该新上架的图书了。

5.1.2 代码实现

调用 indexwriter.adddocument(doc)添加索引。(参考入门示例中的创建索引)

5.2 删除索引(文档)

5.2.1需求

某些图书不再出版销售了,我们需要从索引库中移除该图书。

5.2.2 代码实现

@test
public void deleteindex() throws ioexception {
    //1.指定索引库目录
    directory directory = fsdirectory.open(new file("c:\\users\\carlo\\onedrive\\workspace\\ideaprojects\\lucene-demo01-start\\lucene").topath());
    //2.创建 indexwriterconfig
    indexwriterconfig indexwriterconfig = new indexwriterconfig(new standardanalyzer());
    //3.创建 indexwriter
    indexwriter indexwriter = new indexwriter(directory, indexwriterconfig);
    //4.删除指定索引
    indexwriter.deletedocuments(new term("name", "java"));
    //5.关闭 indexwriter
    indexwriter.close();
}

5.2.3 清空索引代码实现

@test
public void deleteallindex() throws ioexception {
    //1.指定索引库目录
    directory directory = fsdirectory.open(new file("c:\\users\\carlo\\onedrive\\workspace\\ideaprojects\\lucene-demo01-start\\lucene").topath());
    //2.创建 indexwriterconfig
    indexwriterconfig indexwriterconfig = new indexwriterconfig(new standardanalyzer());
    //3.创建 indexwriter
    indexwriter indexwriter = new indexwriter(directory, indexwriterconfig);
    //4.删除所有索引
    indexwriter.deleteall();
    //5.关闭 indexwriter
    indexwriter.close();
}

5.3 更新索引(文档)

5.3.1 说明

lucene更新索引比较特殊,是先删除满足条件的文档,再添加新的文档。

5.3.2 代码实现

@test
public void updateindex() throws ioexception {
    //1.指定索引库目录
    directory directory = fsdirectory.open(new file("c:\\users\\carlo\\onedrive\\workspace\\ideaprojects\\lucene-demo01-start\\lucene").topath());
    //2.创建 indexwriterconfig
    indexwriterconfig indexwriterconfig = new indexwriterconfig(new standardanalyzer());
    //3.创建 indexwriter
    indexwriter indexwriter = new indexwriter(directory, indexwriterconfig);
    //4.创建新加的文档对象
    document document = new document();
    document.add(new textfield("name", "testupdate", field.store.yes));
    //5.修改指定索引为新的索引
    indexwriter.updatedocument(new term("name", "java"), document);
    //6.关闭 indexwriter
    indexwriter.close();
}

6 搜索

问题:我们在入门示例中,已经知道lucene是通过indexsearcher对象,来执行搜索的。在实际的开发中,我们的查询的业务是相对复杂的,比如我们在通过关键词查找的时候,往往进行价格、商品类别的过滤。而lucene提供了一套查询方案,供我们实现复杂的查询。

6.1 创建查询的两种方法

执行查询之前,必须创建一个查询query查询对象。query自身是一个抽象类,不能实例化,必须通过其它的方式来实现初始化。在这里,lucene提供了两种初始化query查询对象的方式。

6.1.1 使用lucene提供query子类

query是一个抽象类,lucene提供了很多查询对象,比如termquery项精确查询numericrangequery数字范围查询等。

6.1.2 使用queryparse解析查询表达式

queryparser会将用户输入的查询表达式解析成query对象实例。如下代码:

queryparser queryparser = new queryparser("name", new standardanalyzer());
query query = queryparser.parse("name:lucene");

6.2 常用的query子类搜索

6.2.1 termquery

特点:查询的关键词不会再做分词处理,作为整体来搜索。代码如下:

@test
public void querybytermquery() throws ioexception {
    query query = new termquery(new term("name", "java"));
    doquery(query);
}

private void doquery(query query) throws ioexception {
    //指定索引库
    directory directory = fsdirectory.open(new file("c:\\users\\carlo\\onedrive\\workspace\\ideaprojects\\lucene-demo01-start\\lucene").topath());
    //创建读取流
    directoryreader reader = directoryreader.open(directory);
    //创建执行搜索对象
    indexsearcher searcher = new indexsearcher(reader);

    //执行搜索
    topdocs topdocs = searcher.search(query, 10);
    system.out.println("共搜索结果:" + topdocs.totalhits);

    //提取文档信息
    //score即相关度。即搜索的关键词和 图书名称的相关度,用来做排序处理
    scoredoc[] scoredocs = topdocs.scoredocs;

    for (scoredoc scoredoc : scoredocs) {
        int docid = scoredoc.doc;
        system.out.println("索引库编号:" + docid);

        //提取文档信息
        document doc = searcher.doc(docid);
        system.out.println(doc.get("name"));
        system.out.println(doc.get("id"));
        system.out.println(doc.get("pricevalue"));
        system.out.println(doc.get("pic"));
        system.out.println(doc.get("description"));

        //关闭读取流
        reader.close();
    }
}

6.2.2 wildcardquery

使用通配符查询

/**
 * 通过通配符查询所有文档
 * @throws ioexception
 */
@test
public void querybywildcardquery() throws ioexception {
    query query = new wildcardquery(new term("name", "*"));
    doquery(query);
}

private void doquery(query query) throws ioexception {
    //指定索引库
    directory directory = fsdirectory.open(new file("c:\\users\\carlo\\onedrive\\workspace\\ideaprojects\\lucene-demo01-start\\lucene").topath());
    //创建读取流
    directoryreader reader = directoryreader.open(directory);
    //创建执行搜索对象
    indexsearcher searcher = new indexsearcher(reader);

    //执行搜索
    topdocs topdocs = searcher.search(query, 10);
    system.out.println("共搜索结果:" + topdocs.totalhits);

    //提取文档信息
    //score即相关度。即搜索的关键词和 图书名称的相关度,用来做排序处理
    scoredoc[] scoredocs = topdocs.scoredocs;

    for (scoredoc scoredoc : scoredocs) {
        int docid = scoredoc.doc;
        system.out.println("索引库编号:" + docid);

        //提取文档信息
        document doc = searcher.doc(docid);
        system.out.println(doc.get("name"));
        system.out.println(doc.get("id"));
        system.out.println(doc.get("pricevalue"));
        system.out.println(doc.get("pic"));
        system.out.println(doc.get("description"));

    }
    //关闭读取流
    reader.close();
}

6.2.3 数字类型的 rangequery

指定数字范围查询.(创建field类型时,注意与之对应),修改建立索引时的 price

/**
 * 将 book 集合封装成 document 集合
 * @param books book集合
 * @return document 集合
 */
public list<document> getdocuments(list<book> books) {
    //创建集合
    list<document> documents = new arraylist<>();

    //循环操作 books 集合
    books.foreach(book -> {
        //创建 document 对象,document 内需要设置一个个 field 对象
        document doc = new document();
        //创建各个 field
        //存储但不分词、不索引
        field id = new storedfield("id", book.getid());
        //存储、分词、索引
        field name = new textfield("name", book.getname(), field.store.yes);
        //float 数字存储、索引
        field price = new floatpoint("price", book.getprice()); //用于数字的区间查询,不会存储,需要额外的 storedfield
        field pricevalue = new storedfield("pricevalue", book.getprice());//用于存储具体价格
        //存储但不分词、不索引
        field pic = new storedfield("pic", book.getpic());
        //分词、索引,但不存储
        field description = new textfield("description", book.getdescription(), field.store.no);
        //将 field 添加到文档中
        doc.add(id);
        doc.add(name);
        doc.add(price);
        doc.add(pricevalue);
        doc.add(pic);
        doc.add(description);

        documents.add(doc);
    });
    return documents;
}

使用对应的 floatpoint 的静态方法,获得 rangequery

/**
 * float 类型的范围查询
 * @throws ioexception
 */
@test
public void querybynumricrangequery() throws ioexception {
    query query = floatpoint.newrangequery("price", 60, 80);
    doquery(query);
}

private void doquery(query query) throws ioexception {
    //指定索引库
    directory directory = fsdirectory.open(new file("c:\\users\\carlo\\onedrive\\workspace\\ideaprojects\\lucene-demo01-start\\lucene").topath());
    //创建读取流
    directoryreader reader = directoryreader.open(directory);
    //创建执行搜索对象
    indexsearcher searcher = new indexsearcher(reader);

    //执行搜索
    topdocs topdocs = searcher.search(query, 10);
    system.out.println("共搜索结果:" + topdocs.totalhits);

    //提取文档信息
    //score即相关度。即搜索的关键词和 图书名称的相关度,用来做排序处理
    scoredoc[] scoredocs = topdocs.scoredocs;

    for (scoredoc scoredoc : scoredocs) {
        int docid = scoredoc.doc;
        system.out.println("索引库编号:" + docid);

        //提取文档信息
        document doc = searcher.doc(docid);
        system.out.println(doc.get("name"));
        system.out.println(doc.get("id"));
        system.out.println(doc.get("pricevalue"));
        system.out.println(doc.get("pic"));
        system.out.println(doc.get("description"));

    }
    //关闭读取流
    reader.close();
}

6.2.4 booleanquery

booleanquery,布尔查询,实现组合条件查询。

@test
public void querybybooleanquery() throws ioexception {
    query pricequery = floatpoint.newrangequery("price", 60, 80);
    query namequery = new termquery(new term("name", "java"));

    //通过 builder 创建 query
    booleanquery.builder booleanquerybuilder = new booleanquery.builder();
    //至少有一个时 occur.must,不然结果为空
    booleanquerybuilder.add(namequery, booleanclause.occur.must_not);
    booleanquerybuilder.add(pricequery, booleanclause.occur.must);
    booleanquery query = booleanquerybuilder.build();

    doquery(query);
}

private void doquery(query query) throws ioexception {
    //指定索引库
    directory directory = fsdirectory.open(new file("c:\\users\\carlo\\onedrive\\workspace\\ideaprojects\\lucene-demo01-start\\lucene").topath());
    //创建读取流
    directoryreader reader = directoryreader.open(directory);
    //创建执行搜索对象
    indexsearcher searcher = new indexsearcher(reader);

    //执行搜索
    topdocs topdocs = searcher.search(query, 10);
    system.out.println("共搜索结果:" + topdocs.totalhits);

    //提取文档信息
    //score即相关度。即搜索的关键词和 图书名称的相关度,用来做排序处理
    scoredoc[] scoredocs = topdocs.scoredocs;

    for (scoredoc scoredoc : scoredocs) {
        int docid = scoredoc.doc;
        system.out.println("索引库编号:" + docid);

        //提取文档信息
        document doc = searcher.doc(docid);
        system.out.println(doc.get("name"));
        system.out.println(doc.get("id"));
        system.out.println(doc.get("pricevalue"));
        system.out.println(doc.get("pic"));
        system.out.println(doc.get("description"));

    }
    //关闭读取流
    reader.close();
}

6.3 通过queryparser搜索

6.3.1 特点

对搜索的关键词,做分词处理。

6.3.2 语法

6.3.2.1 基础语法

域名:关键字 如: name:java

6.3.2.2 组合条件语法

  • 条件1 and 条件2
  • 条件1 or 条件2
  • 条件1 not 条件2

例如: query query = queryparser.parse("java not 编");

6.3.3 queryparser

@test
public void querybyqueryparser() throws ioexception, parseexception {
    //创建分词器
    standardanalyzer standardanalyzer = new standardanalyzer();
    /**
     * 创建查询解析器
     * 参数一: 默认搜索的域。
     *         如果在搜索的时候,没有特别指定搜索的域,则按照默认的域进行搜索
     *         指定搜索的域的方式:   域名:关键词  如:  name:java
     * 参数二: 分词器,对关键词做分词处理
     */
    queryparser queryparser = new queryparser("description", standardanalyzer);
    query query = queryparser.parse("java 教程");
    doquery(query);
}

6.3.4 multifieldqueryparser

通过mulitfieldqueryparse对多个域查询。

@test
public void querybymultifieldqueryparser() throws parseexception, ioexception {
    //1.定义多个搜索的域
    string[] fields = {"name", "description"};
    //2.加载分词器
    standardanalyzer standardanalyzer = new standardanalyzer();
    //3.创建 multifieldqueryparser 实例对象
    multifieldqueryparser multifieldqueryparser = new multifieldqueryparser(fields, standardanalyzer);
    query query = multifieldqueryparser.parse("java");

    doquery(query);
}

7 中文分词器

7.1 什么是中文分词器

学过英文的都知道,英文是以单词为单位的,单词与单词之间以空格或者逗号句号隔开。标准分词器,无法像英文那样按单词分词,只能一个汉字一个汉字来划分。所以需要一个能自动识别中文语义的分词器。

7.2 lucene自带的中文分词器

7.2.1 standardanalyzer:

单字分词:就是按照中文一个字一个字地进行分词。如:“我爱中国”

效果:“我”、“爱”、“中”、“国”。

7.2.2 cjkanalyzer

二分法分词:按两个字进行切分。如:“我是中国人”

效果:“我是”、“是中”、“中国”“国人”。

7.2.3 smartchineseanalyzer

官方提供的智能中文识别,需要导入新的 jar 包

Lucene 全文检索

@test
public void createindexbychinese () {
    bookdao dao = new bookdao();
    //该分词器用于中文分词
    smartchineseanalyzer smartchineseanalyzer = new smartchineseanalyzer();
    //创建索引
    //1. 创建索引库存储目录
    try (directory directory = fsdirectory.open(new file("c:\\users\\carlo\\onedrive\\workspace\\ideaprojects\\lucene-demo01-start\\lucene").topath())) {
        //2. 创建 indexwriterconfig 对象
        indexwriterconfig ifc = new indexwriterconfig(smartchineseanalyzer);
        //3. 创建 indexwriter 对象
        indexwriter indexwriter = new indexwriter(directory, ifc);
        //4. 通过 indexwriter 对象添加文档
        indexwriter.adddocuments(dao.getdocuments(dao.listall()));
        //5. 关闭 indexwriter
        indexwriter.close();

        system.out.println("完成索引库创建");
    } catch (ioexception e) {
        e.printstacktrace();
    }
}

效果如图:

Lucene 全文检索