• Lucene 全文检索


    基于 lucene 8

    1 Lucene简介

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

    1.1 全文检索(Full-text Search)

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

    1.2 Lucene实现全文检索的流程

    image

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

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

    2 入门示例

    2.1 需求

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

    2.2 配置步骤说明

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

    2.3 配置步骤

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

    image

    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是使用文档类型来封装数据的,所有需要先将采集的数据转换成文档类型。其格式为:

    image

    修改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 工具查看结果

    image

    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();
        }
    }
    

    结果

    image

    3 分词

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

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

    image

    从上图中,我们发现:

    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常用类型

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

    image

    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;
    }
    

    结果

    image

    image

    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 包

    image

    @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();
        }
    }
    

    效果如图:

    image

  • 相关阅读:
    Windows 2008R2 安装PostgreSQL 11.6
    Redis-基础介绍
    SQL Server中的GAM页和SGAM页
    linux读写相关
    String 和 Stringbuild
    JVM(六)如何执行方法调用
    dubbo学习(三)实现细节
    dubbo学习(二)SPI
    spring boot
    MySQL学习(二十一)锁
  • 原文地址:https://www.cnblogs.com/carlosouyang/p/11344130.html
Copyright © 2020-2023  润新知