Lucene

2020-03-30

一、什么是全文检索

image20200325175922112.png

1.1 数据的分类

  1)结构化数据

    格式固定、长度固定、数据类型固定

    例如数据库中的数据

  2)非结构化数据

    word文档、pdf文档、邮件、html

    格式不固定、长度不固定、数据类型不固定

1.2 数据的查询

  1)结构化数据的查询

    SQL语句,查询结构化数据的方法。简单、速度快。

  2)非结构化数据的查询

    eg:从文本文件中找出包含spring单词的文件

      a.目测

      b.是用程序把文档读取到内存中,然后匹配字符串。顺序扫描。

      c.把非结构化数据变成结构化数据

        先根据空格拆分字符串,得到一个单词列表,基于单词列表创建一个索引。然后查询索引,根据单词和文档的对应关系找到文档列表。这个过程叫做全文检索。

        索引:一个为了提高查询速度,创建某种数据结构的集合。

1.3 全文检索

  先创建索引然后查询索引的过程叫做全文检索。

  索引一次创建可以多次使用。表现为每次查询速度很快。

二、全文检索的应用场景

2.1 搜索引擎

  百度、360搜索、谷歌、搜狗

2.2 站内搜索

  论坛搜索、微博、文章搜索

2.3 电商搜索

  淘宝搜索、京东搜索

2.4 只要是有搜索的地方就可以使用全文检索技术

三、什么是Lucene

Lucene是一个基于Java开发的全文检索工具包。

四、Lucene实现全文检索的流程

4.1 创建索引

  1)获得文档

    原始文档:要基于哪些数据进行全文检索,那么这些数据就是就是原始文档

    搜索引擎:使用爬虫获得原始文档

    站内搜索:数据库中的数据

  2)构建文档对象

    对应每个原始文档创建一个Document对象

    每个Document对象中包含多个域(field)

    域中保存的就是原始文档数据

      域的名称

      域的值

    每个文档都有一个唯一的编号,就是文档id

  3)分析文档

    就是分词的过程

    1、根据空格进行字符串拆分、得到一个单词列表

    2、把单词统一转换成小写或大写

    3、去除标点符号

    4、去除停用词(无意义的词)

    5、每个关键词都封装成一个Term对象

      Term中包含两部分内容:

        关键词所在的域

        关键词本身

      不同的域中拆分出来的相同的关键词是不同的Term

  4)创建索引

    基于关键词列表创建一个索引,保存到索引库中。

    索引库中:

      索引

      Document对象

      关键词和文档的对应关系

    通过词语找文档,这种索引的结构叫做倒排索引结构

4.2 查询索引

  1)用户查询接口

    用户输入查询条件的地方

    eg:百度的搜索框

  2)把关键词封装成一个查询对象

    要查询的域

    要搜索的关键词

  3)执行查询

    根据要查询的关键词到对应的域上进行搜索

    找到关键词,根据关键词找到对应的文档

  4)渲染结果

    根据文档的id找到文档对象

    对关键词进行高亮显示

    分页处理

    ...

    最终展示给用户

五、入门程序

5.1 工程搭建

环境:Lucene、JDK1.8

  步骤:

    1.创建一个java工程

      image20200330090348306.png

    2.添加依赖

      lucene-analyzers-common

      lucene-core

      commons-io

<dependencies>
        <dependency>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-analyzers-common</artifactId>
            <version>8.5.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-core</artifactId>
            <version>8.5.0</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>
    </dependencies>

5.2 创建索引

  步骤:

    1.创建一个Directory对象,指定索引库保存的位置。

    2.基于Directory对象创建一个IndexWriter对象。

    3.读取磁盘上的文件,对应每个文件创建一个文档对象。

    4.向文档对象中添加域。

    5.把文档对象写入索引库。

    6.关闭IndexWriter对象。

  代码:

public void createIndex() throws Exception {
        //1.创建一个Directory对象,指定索引库保存的位置。
        //把索引库保存在内存中
        //Directory directory = new RAMDirectory();
        //把索引库保存在磁盘
        Directory directory = FSDirectory.open(new File("D:\\").toPath());
        //2.基于Directory对象创建一个IndexWriter对象。
        IndexWriter indexWriter = new IndexWriter(directory, new IndexWriterConfig());
        //3.读取磁盘上的文件,对应每个文件创建一个文档对象。
        File dir = new File("D:\\java");
        File[] files = dir.listFiles();
        for (File file : files) {
            //取文件名
            String fileName = file.getName();
            //文件的路径
            String filePath = file.getPath();
            //文件内容
            String fileContent = FileUtils.readFileToString(file, "UTF-8");
            //文件大小
            long fileSize = FileUtils.sizeOf(file);
            //创建Field
            //参数1:域的名称  参数2:域的内容  参数3:是否存储
            Field fieldName = new TextField("name", fileName, Field.Store.YES);
            Field fieldPath = new TextField("path", filePath, Field.Store.YES);
            Field fieldContent = new TextField("content", fileContent, Field.Store.YES);
            Field fieldSize = new TextField("size", fileSize + "", Field.Store.YES);
            //创建文档对象
            Document document = new Document();
            //4.向文档对象中添加域。
            document.add(fieldName);
            document.add(fieldPath);
            document.add(fieldContent);
            document.add(fieldSize);
            //5.把文档对象写入索引库。
            indexWriter.addDocument(document);
        }
        //6.关闭IndexWriter对象。
        indexWriter.close();
    }

5.3 查询索引库

  步骤:

    1.创建一个Director对象,指定索引库的位置

    2.创建一个IndexReader对象

    3.创建一个IndexSearcher对象,构造方法中的参数indexReader对象

    4.创建一个Query对象,TermQuery

    5.执行查询,得到一个TopDocs对象

    6.取查询结果的总记录数

    7.取文档列表

    8.打印文档中的内容

    9.关闭IndexReader对象

  代码:

public void searchIndex() throws Exception {
        //1.创建一个Director对象,指定索引库的位置
        Directory directory = FSDirectory.open(new File("D:\\").toPath());
        //2.创建一个IndexReader对象
        IndexReader indexReader = DirectoryReader.open(directory);
        //3.创建一个IndexSearcher对象,构造方法中的参数indexReader对象
        IndexSearcher indexSearcher = new IndexSearcher(indexReader);
        //4.创建一个Query对象,TermQuery
        Query query = new TermQuery(new Term("content","java"));
        //5.执行查询,得到一个TopDocs对象
        //参数1:查询对象  参数2:查询返回的最大记录数
        TopDocs topDocs = indexSearcher.search(query,10);
        //6.取查询结果的总记录数
        System.out.println("查询总记录数:"+topDocs.totalHits);
        //7.取文档列表
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        //8.打印文档中的内容
        for (ScoreDoc scoreDoc : scoreDocs) {
            //取文档id
            int docId = scoreDoc.doc;
            //根据id取文档对象
            Document document = indexSearcher.doc(docId);
            System.out.println(document.get("name"));
            System.out.println(document.get("path"));
            System.out.println(document.get("size"));
            System.out.println(document.get("content"));
            System.out.println("-----------------------------------");
        }
        //9.关闭IndexReader对象
        indexReader.close();
    }
评论
发表评论