Lucene架构学习

Lucene

为什么使用Lucene(全文检索)

应用场景:搜索引擎(全文检索)

站内搜索:淘宝站内商品的搜索,博客园找找看 等

在这里插入图片描述

基本概念

什么是全文检索

全文检索是计算机程序通过扫描文章中的每一个词,对每一个词建立一个索引,指明该词在文章中出现的次数和位置。当用户查询时根据建立的索引查找,类似于通过字典的检索字表查字的过程

建立索引的过程:全文检索是计算机程序通过扫描文章中的每一个词,对每一个词建立一个索引,指明该词在文章中出现的次数和位置

在这里插入图片描述

查询过程:当用户查询时根据建立的索引查找,类似于通过字典的检索字表查字的过程

在这里插入图片描述

全文检索(Full-Text Retrieval)以文本作为检索对象,找出含有指定词汇的文本。全面、准确和快速是衡量全文检索系统的关键指标。

全文检索的特点
  1. 只处理文本数据

  2. 不处理语义,数据的匹配,不做语义分析

  3. 搜索时英文不区分大小写

  4. 结果列表有相关度排序

    相关度分数:搜索关键词和结果数据 匹配程度 Lucene自动计算 可以人为修改

全文检索和模糊匹配的区别

模糊匹配的缺点

  1. 数据不够准备 模糊匹配要求 搜索关键词的每一个字以及顺序 都需要和数据库中的数据 一一对应 才能够找结果

    需求:因为用户输入的关键词 千奇百怪 不可能做到和数据库的原始数据 一一对应 所以通过模糊匹配很难的到结果

  2. 几乎不能使用上索引

    除了右模糊 都使用不了索引【执行计划是确定一条sql能不能使用索引的标准】

全文检索的优点

  1. 搜索结果更加准确 全面

  2. 效率更高

    文检索的速度大大快于SQL的like搜索的速度。这是因为查询方式不同造成的,以查字典举例:数据库的like就是一页一页的翻,一行一行的找,而全文检索是先查目录,得到结果所在的页码,再直接翻到这一页

  3. 相关度排序

    查出的结果没有相关度排序,不知道我想要的结果在哪一页。我们在使用百度搜索时,一般不需要翻页,为什么?因为百度做了相关度排序:为每一条结果打一个分数,这条结果越符合搜索条件,得分就越高,叫做相关度得分,结果列表会按照这个分数由高到低排列,所以第1页的结果就是我们最想要的结果

基本使用

导入依赖
<!--核心依赖-->
<dependency>
    <groupId>org.apache.lucene</groupId>
    <artifactId>lucene-core</artifactId>
    <version>4.4.0</version>
</dependency>
<!--分词器 对文本做分词处理-->
<dependency>
    <groupId>org.apache.lucene</groupId>
    <artifactId>lucene-analyzers-common</artifactId>
    <version>4.4.0</version>
</dependency>
<!--智能分词器-->
<dependency>
    <groupId>org.apache.lucene</groupId>
    <artifactId>lucene-analyzers-smartcn</artifactId>
    <version>4.4.0</version>
</dependency>
<!--查询-->
<dependency>
    <groupId>org.apache.lucene</groupId>
    <artifactId>lucene-queryparser</artifactId>
    <version>4.4.0</version>
</dependency>
索引的创建
/**
 * 创建索引
 */
@Test
public void test1() throws IOException {
    Integer id = 1;
    String title = "背影";
    String author = "朱自清";
    String content = "你站在这里不要动,我去给你买几个橘子";

    /**
     * 1.准备文章数据  将数据封装在Document对象 文档对象
     * Document add方法封装数据  参数为Field对象
     * 八种基本类型和StringField 暂时可以认为不分词
     * TextField 文本Field 会做分词处理 需要被查询的数据 要定义为文本Field
     * Field
     *  参数1  属性名
     *  参数2  属性值
     *  参数3  Field.Store.YES 固定写法
     */
    Document document = new Document();

    document.add(new IntField("id",id, Field.Store.YES));
    document.add(new TextField("title",title, Field.Store.YES));
    document.add(new TextField("author",author, Field.Store.YES));
    document.add(new TextField("content",content, Field.Store.YES));

    /**
     * 2.通过程序扫描文章数据创建索引
     * IndexWriter 索引写出对象(相当于一个流) 把文档对象写出到索引库 在写出的过程中会自动做分词处理 并且 创建索引
     * IndexWriter 对象
     * 参数1 定义索引库的位置  FSDirectory directory = FSDirectory.open(new File("E://lucene"));
     * 参数2 定义索引创建的配置 索引配置对象
     * IndexWriterConfig
     * 参数1 当前Lucene的版本号
     * 参数2 分词器对象 分词器:对文本做分词处理    StandardAnalyzer 标准分词器   StandardAnalyzer analyzer = new StandardAnalyzer(Version.LUCENE_44);
     */
    FSDirectory directory = FSDirectory.open(new File("E://lucene"));
    StandardAnalyzer analyzer = new StandardAnalyzer(Version.LUCENE_44);
    IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_44,analyzer);
    IndexWriter indexWriter = new IndexWriter(directory,config);

    /**
     * 将文章数据 给 索引写出对象
     */
    indexWriter.addDocument(document);
    /**
     * 将文章数据提交到索引库  会自动做分词处理 并且 创建索引
     */
    indexWriter.commit();
    /**
     * 释放资源
     */
    indexWriter.close();
}

通过搜索关键词查询
@Test
public void test2() throws ParseException, IOException {
    /**
     * 1.准备搜索关键词
     */
    String keywords = "朱自清的文章";
    /**
     * 2.处理搜索关键词
     * MultiFieldQueryParser 多列属性查询处理的对象
     * 参数1 Lucene的版本号
     * 参数2 属性名的数组
     * 参数3 分词器对象  需要保证和创建索引时候用的一个分词器
     * parse() 处理关键词 得到一个Query对象
     */
    String[] fields = {"title","author","content"};
    StandardAnalyzer analyzer = new StandardAnalyzer(Version.LUCENE_44);
    MultiFieldQueryParser queryParser = new MultiFieldQueryParser(Version.LUCENE_44,fields,analyzer);
    Query query = queryParser.parse(keywords);

    /**
     * 3.通过处理好的关键词 去 索引库中查询 得到索引(索引词+最终数据的位置信息id)
     */
    DirectoryReader reader = DirectoryReader.open(FSDirectory.open(new File("E://lucene")));
    IndexSearcher searcher = new IndexSearcher(reader);
    /**
     * search 方法
     * 参数1 Query 对象
     * 参数2 期望的结果条数
     *
     * TopDocs 结果集 包含了结果的数据(索引)
     */
    TopDocs topDocs = searcher.search(query, 10);
    /**
     * 处理结果集
     * scoreDocs 包含有分数 和 文档对象的id(原始数据)(位置信息)
     */
    ScoreDoc[] scoreDocs = topDocs.scoreDocs;
    /**
     * 4.通过位置信息 得到数据
     */
    for (ScoreDoc scoreDoc : scoreDocs) {
        System.out.println(scoreDoc.score);
        /**
         * docId 文档对象在索引库中的位置信息
         */
        int docId = scoreDoc.doc;
        /**
         * 通过位置信息 得到数据 Document
         */
        Document document = searcher.doc(docId);

        String id = document.get("id");
        System.out.println(id);
        String title = document.get("title");
        System.out.println(title);
        String author = document.get("author");
        System.out.println(author);
        String content = document.get("content");
        System.out.println(content);

    }
}
删除索引库
第一天作业
  1. 练习demo 【两遍】

  2. 实现我给的接口

在这里插入图片描述

作业讲解

在这里插入图片描述

 /**
     * 将所有文章数据添加至索引库
     */
    @Test
    public void test4() throws IOException {
//        文章数据
        List<CmfzArticle> cmfzArticles = cmfzArticleDao.selectList(null);
        /**
         * 1.封装数据 到文档对象中 List<Document>
         */
        List<Document> documents = new ArrayList<>();

        for (CmfzArticle cmfzArticle : cmfzArticles) {
            Document document = new Document();

            document.add(new IntField("articleId",cmfzArticle.getArticleId(), Field.Store.YES));
            /**
             * StringField 不分词
             * TextField 会分词处理 需要被查询的数据 要定义为文本Field
             */
            document.add(new TextField("articleName",cmfzArticle.getArticleName(), Field.Store.YES));
            document.add(new TextField("articleContent",cmfzArticle.getArticleContent(), Field.Store.YES));
            /**
             * 时间 可以 long 类型  String
             */
            document.add(new LongField("articleDate",cmfzArticle.getArticleDate().getTime(), Field.Store.YES));
            String s = DateTools.dateToString(cmfzArticle.getArticleDate(), DateTools.Resolution.DAY);
            System.out.println(s);


            documents.add(document);
        }

        /**
         * 2.通过索引写出对象 写出到索引库
         */
        FSDirectory directory = FSDirectory.open(new File("E://lucene"));
        IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_44,new StandardAnalyzer(Version.LUCENE_44));
        IndexWriter indexWriter = new IndexWriter(directory,config);

        indexWriter.addDocuments(documents);

        indexWriter.commit();


        indexWriter.close();
    }

    /**
     * 查询上师文章
     */
    @Test
    public void test5() throws ParseException, IOException {
        /**
         * 1.准备搜索关键词
         */
        String keyWords = "曾经有一位上师";

        /**
         * 2.处理关键词
         */
        String[] ss = {"articleName","articleContent"};
        MultiFieldQueryParser parser = new MultiFieldQueryParser(Version.LUCENE_44,ss,new StandardAnalyzer(Version.LUCENE_44));
        Query query = parser.parse(keyWords);

        /**
         * 3.查询
         */
        DirectoryReader reader = DirectoryReader.open(FSDirectory.open(new File("E://lucene")));
        IndexSearcher indexSearcher = new IndexSearcher(reader);
        TopDocs topDocs = indexSearcher.search(query, 10);

        /**
         * 4.处理结果集
         */
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        for (ScoreDoc scoreDoc : scoreDocs) {
            int docId = scoreDoc.doc;

            /**
             * 通过id 查询数据
             */
            Document document = indexSearcher.doc(docId);

            String articleId = document.get("articleId");
            String articleName = document.get("articleName");
            String articleContent = document.get("articleContent");
            String articleDate = document.get("articleDate");

            CmfzArticle cmfzArticle = new CmfzArticle();
            cmfzArticle.setArticleId(Integer.parseInt(articleId));
            cmfzArticle.setArticleName(articleName);
            cmfzArticle.setArticleContent(articleContent);
//            Long类型转时间
            long l = Long.parseLong(articleDate);
            Date date = new Date(l);
            cmfzArticle.setArticleDate(date);
//            日期转String
            String s = DateTools.dateToString(date, DateTools.Resolution.DAY);
            System.out.println(s);

            System.out.println(cmfzArticle);
        }
    }
索引库的基本结构

在这里插入图片描述

封装工具类
package com.baizhi.util;

import com.baizhi.entity.CmfzArticle;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.*;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;

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

public class LuceneUtil {
    private static FSDirectory directory = null;
    private static Analyzer analyzer = null;

    /**
     * 做成员变量的初始化
     */
    static {
        try {
            directory = FSDirectory.open(new File("E://lucene"));
            analyzer = new StandardAnalyzer(Version.LUCENE_44);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取 IndexWriter
     * @return
     */
    public static IndexWriter getIndexWriter(){
        IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_44,analyzer);
        IndexWriter indexWriter = null;
        try {
            indexWriter = new IndexWriter(directory,config);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return indexWriter;
    }

    /**
     * 文章对象 转换 文档对象
     */
    public static List<Document> cmfzArticleToDocument(List<CmfzArticle> cmfzArticles){
        List<Document> documents = new ArrayList<>();

        for (CmfzArticle cmfzArticle : cmfzArticles) {
            Document document = new Document();

            document.add(new IntField("articleId",cmfzArticle.getArticleId(), Field.Store.YES));
            /**
             * StringField 不分词
             * TextField 会分词处理 需要被查询的数据 要定义为文本Field
             */
            document.add(new TextField("articleName",cmfzArticle.getArticleName(), Field.Store.YES));
            document.add(new TextField("articleContent",cmfzArticle.getArticleContent(), Field.Store.YES));
            /**
             * 时间 可以 long 类型  String
             */
            document.add(new LongField("articleDate",cmfzArticle.getArticleDate().getTime(), Field.Store.YES));

            documents.add(document);
        }

        return documents;
    }

    /**
     * 处理关键词
     * @param keyWords
     * @return
     */
    public static Query parsekeyWords(String keyWords){
        String[] ss = {"articleName","articleContent"};
        MultiFieldQueryParser parser = new MultiFieldQueryParser(Version.LUCENE_44,ss,analyzer);
        Query query = null;
        try {
            query = parser.parse(keyWords);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return query;
    }

    /**
     * 获取IndexSearcher
     */
    public static IndexSearcher getIndexSearcher(){
        DirectoryReader reader = null;
        try {
            reader = DirectoryReader.open(directory);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new IndexSearcher(reader);
    }

    /**
     * 处理结果集  得到文章对象集合
     */
    public static List<CmfzArticle> scoreDocToCmfzArticle(ScoreDoc[] scoreDocs,IndexSearcher indexSearcher){
        List<CmfzArticle> cmfzArticles = new ArrayList<>();

        for (ScoreDoc scoreDoc : scoreDocs) {
            int docId = scoreDoc.doc;

            /**
             * 通过id 查询数据
             */
            Document document = null;
            try {
                document = indexSearcher.doc(docId);
            } catch (IOException e) {
                e.printStackTrace();
            }
//            获取数据
            String articleId = document.get("articleId");
            String articleName = document.get("articleName");
            String articleContent = document.get("articleContent");
            String articleDate = document.get("articleDate");

//            封装对象
            CmfzArticle cmfzArticle = new CmfzArticle();
            cmfzArticle.setArticleId(Integer.parseInt(articleId));
            cmfzArticle.setArticleName(articleName);
            cmfzArticle.setArticleContent(articleContent);
//            Long类型转时间
            long l = Long.parseLong(articleDate);
            Date date = new Date(l);
            cmfzArticle.setArticleDate(date);

//            封装集合
            cmfzArticles.add(cmfzArticle);
        }

        return cmfzArticles;
    }
}

作业:

  1. 封装工具类
  2. 使用id 删除一个上师的数据
删除一个 和 修改
/**
 * 删除一个 按照id  int 需要使用查询Int的Query
 */
@Test
public void test6() throws IOException {
    IndexWriter indexWriter = LuceneUtil.getIndexWriter();
    /**
     * Term 词语 术语 学期  在Lucene指 一个不能被分词的词语 最小单位  上师  针对的是StringField进行查询
     * Query
     *
     * 方法1:通过int删除
     * Range方法 是一个范围
     * 参数1 属性名
     * 参数2 开始id
     * 参数3 结束id
     * 参数4 true false 包不包含开始id
     * 参数5 true false 包不包含结束id
     *
     * 方法2:通过String删除 假设id=123 "123"
     * 1.存索引的时候 需要保证Id用的是 StringField
     * 2.删除的时候 使用TermQuery来进行删除
     */

    NumericRangeQuery<Integer> query = NumericRangeQuery.newIntRange("articleId", 1, 1, true, true);

    TermQuery termQuery = new TermQuery(new Term("articleId", "1"));

    indexWriter.deleteDocuments(termQuery);

    indexWriter.commit();

    indexWriter.close();
}

/**
 * 修改
 */
@Test
public void test7() throws IOException {
    IndexWriter indexWriter = LuceneUtil.getIndexWriter();
    /**
     * 参数1 Term对象 要修改那个id对应的数据
     * 参数2 Document对象  要更新的数据
     * 参数3 分词器
     */
    Term articleId = new Term("articleId", "2");

    Document document = new Document();
    document.add(new StringField("articleId","2", Field.Store.YES));
    /**
     * StringField 不分词
     * TextField 会分词处理 需要被查询的数据 要定义为文本Field
     */
    document.add(new TextField("articleName","上师对弟子的评价与期望", Field.Store.YES));
    document.add(new TextField("articleContent","春眠不觉晓", Field.Store.YES));
    /**
     * 时间 可以 long 类型  String
     */
    document.add(new LongField("articleDate",new Date().getTime(), Field.Store.YES));
    
    indexWriter.updateDocument(articleId,document,LuceneUtil.analyzer);

    indexWriter.commit();

    indexWriter.close();
}
分词器讲解

分词器:按照一定的规则,解析文本,提取关键词

分词规则:

  1. 关键词 会被创建为索引的词 例如:橘子 背影 等
  2. 停用词 不需要被创建索引的词 例如:的 呢 了 呵 等

什么是关键词 什么是停用词 是自定义的

不同的分词器 有不同的分词规则

测试常见的分词器

package com.baizhi.lucene;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.cjk.CJKAnalyzer;
import org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.util.Version;
import org.junit.Test;
import org.wltea.analyzer.lucene.IKAnalyzer;

import java.io.IOException;
import java.io.StringReader;

/**
 *
 */
public class AnalyzerTest {
    private String text = "百知教育,上师,窃书不能算偷……窃书!……读书人的事,能算偷么?”接连便是难懂的话";

    /**
     * 测试分词规则 标准分词器
     * 单个分词  每个字儿都是关键词
     *
     */
    @Test
    public void test1() throws IOException {
        test(new StandardAnalyzer(Version.LUCENE_44),text);
    }

    /**
     * 智能中文分词器
     */
    @Test
    public void test2() throws IOException {
        test(new SmartChineseAnalyzer(Version.LUCENE_44),text);
    }

    /**
     * 中日韩分词器
     * 分词规则 两两分词
     */
    @Test
    public void test3() throws IOException {
        test(new CJKAnalyzer(Version.LUCENE_44),text);
    }

    /**
     * ik分词器  庖丁分词器
     */
    @Test
    public void test4() throws IOException {
        IKAnalyzer ikAnalyzer = new IKAnalyzer();

        test(ikAnalyzer,text);
    }
    /**
     *  该方法会把分词的结果打印出来
     * @param analyzer  分词器对象
     * @param text 文本  要进行分词的文本
     * @throws IOException
     */
    public static void  test(Analyzer analyzer, String text) throws IOException {
        System.out.println("当前分词器:--->"+analyzer.getClass().getName());
        TokenStream tokenStream = analyzer.tokenStream("content", new StringReader(text));
        tokenStream.addAttribute(CharTermAttribute.class);
        tokenStream.reset();
        while(tokenStream.incrementToken()){
            CharTermAttribute attribute = tokenStream.getAttribute(CharTermAttribute.class);
            System.out.println(attribute.toString());
        }
        tokenStream.end();
        int a = 1;
    }
}

自定义分词规则(使用IK分词器)

  1. 导入依赖

    <!--ik中文分词器-->
    <dependency>
        <groupId>com.janeluo</groupId>
        <artifactId>ikanalyzer</artifactId>
        <version>2012_u6</version>
    </dependency>
    
  2. 导入配置文件

在这里插入图片描述

  1. 在配置文件(词典)中自定义分词规则

在这里插入图片描述

在这里插入图片描述

  1. 直接在项目中使用

在这里插入图片描述

高亮展示

在这里插入图片描述

问题:

  1. 如何实现表示? <font color='red'>上师</font>
  2. 高亮展示添加给谁? 搜索关键词处理的结果
  3. 在什么地方给 关键词 添加 标签? 数据查询出来之后

代码实现

  1. 导入依赖

    <!--高亮器-->
    <dependency>
        <groupId>org.apache.lucene</groupId>
        <artifactId>lucene-highlighter</artifactId>
        <version>4.4.0</version>
    </dependency>
    
  2. 修改查询的代码

    1. 在查询过程中创建一个高亮器
      在这里插入图片描述

    2. 使用高亮器处理查询结果

    在这里插入图片描述

    1. 示例代码【没有封装工具类】

      /**
       * 搜索
       * @throws ParseException
       * @throws IOException
       */
      @Test
      public void test2() throws ParseException, IOException, InvalidTokenOffsetsException {
          /**
           * 1.准备搜索关键词
           */
          String keywords = "朱自清的文章";
          /**
           * 2.处理搜索关键词
           * MultiFieldQueryParser 多列属性查询处理的对象
           * 参数1 Lucene的版本号
           * 参数2 属性名的数组
           * 参数3 分词器对象  需要保证和创建索引时候用的一个分词器
           * parse() 处理关键词 得到一个Query对象
           */
          String[] fields = {"title","author","content"};
          StandardAnalyzer analyzer = new StandardAnalyzer(Version.LUCENE_44);
          MultiFieldQueryParser queryParser = new MultiFieldQueryParser(Version.LUCENE_44,fields,analyzer);
          Query query = queryParser.parse(keywords);
      
          /**
           * 3.通过处理好的关键词 去 索引库中查询 得到索引(索引词+最终数据的位置信息id)
           */
          DirectoryReader reader = DirectoryReader.open(FSDirectory.open(new File("E://lucene")));
          IndexSearcher searcher = new IndexSearcher(reader);
      
          /**
           * search 方法
           * 参数1 Query 对象
           * 参数2 期望的结果条数
           *
           * TopDocs 结果集 包含了结果的数据(索引)
           */
          TopDocs topDocs = searcher.search(query, 10);
          /**
           * 处理结果集
           * scoreDocs 包含有分数 和 文档对象的id(原始数据)(位置信息)
           */
          ScoreDoc[] scoreDocs = topDocs.scoreDocs;
      
      
          /**
           * 创建高亮器
           * 参数1 高亮规则  要添加的样式 `<font color='red'></font>`
           * 参数2 搜索关键词分词的结果 再处理  QueryScorer:处理搜索关键词
           */
          SimpleHTMLFormatter formatter = new SimpleHTMLFormatter("<font color='red'>", "</font>");
          QueryScorer queryScorer = new QueryScorer(query);
          Highlighter highlighter = new Highlighter(formatter,queryScorer);
      
          /**
           * 4.通过位置信息 得到数据
           */
          for (ScoreDoc scoreDoc : scoreDocs) {
              System.out.println(scoreDoc.score);
              /**
               * docId 文档对象在索引库中的位置信息
               */
              int docId = scoreDoc.doc;
              /**
               * 通过位置信息 得到数据 Document
               */
              Document document = searcher.doc(docId);
      
              String id = document.get("id");
              System.out.println(id);
              String title = document.get("title");
              System.out.println(title);
              String author = document.get("author");
              /**
               * 通过高亮器 处理搜索结果
               * 参数1 分词器
               * 参数2 属性名
               * 参数3 被处理文本
               */
              String author1 = highlighter.getBestFragment(LuceneUtil.analyzer, "author", author);
              System.out.println(author);
              System.out.println(author1);
      
              String content = document.get("content");
              System.out.println(content);
              String date = document.get("date");
              long l = Long.parseLong(date);
              Date date1 = new Date(l);
              System.out.println(date1);
              String s = DateTools.dateToString(date1, DateTools.Resolution.DAY);
              System.out.println(s);
      
          }
      }
      

高亮的问题

在这里插入图片描述

解决:非空判断

在这里插入图片描述

项目集成

作业:

  1. 将之前的分词器替换IK分词器
  2. 练习高亮的代码
  3. 完成项目集成 方案1

方案1【作业】:

在这里插入图片描述

  1. 重置索引库功能
  2. 高级检索功能 就是全文检索 将查询的结果展示在table中(不需要考虑分页)

方案2【了解】:

  1. 搜索引擎的入口

在这里插入图片描述

  1. 搜索结果展示页 【内容来自索引库】

在这里插入图片描述

  1. 结果详情页 【内容来自数据库】

在这里插入图片描述

问题解决

在这里插入图片描述

总结

  1. 应用场景出发:用户输入的关键词 千奇百怪 不可能做到和数据库的原始数据 一一对应 所以通过模糊匹配很难的到结果
  2. 模糊匹配的缺点
  3. 全文检索
    1. 索引创建
    2. 查询过程
  4. 全文特点
  5. 代码环节
    1. 索引的创建
      1. 准备数据 将数据封装在 Document 对象 文档对象
      2. 将数据写出到 索引库 在写的同时Lucene会自动的做分词处理 并且 创建索引
        1. 索引库位置的定义
        2. 索引写出的参数的定义 版本 分词器
      3. 将文档对象交给 索引写出对象
      4. 提交到索引库
      5. 释放资源
    2. 通过关键词查询
      1. 获取搜索关键词
      2. 处理搜索关键词
        1. 需要定义 要查询哪些属性 使用什么分词器
        2. 需要保证 查询时的分词器 和 创建索引时的分词器 是同一个
      3. 通过索引查询对象 读取索引库 直接调用方法查询 得到结果集
      4. 处理结果集 得到 文档对象的id (位置信息)
      5. 通过文档对象的id 再次查询 得到原始的数据 (Document 对象 )
    3. 索引库的数据结构
      1. 两个区域:索引区和元数据区
      2. Field.Store.YES 和 Field.Store.NO 区别
    4. 封装工具类 【常规操作】
    5. 删除和修改
      1. 根据 id 删除
        1. IntField
        2. StringField
    6. 分词器
      1. 分词规则
        1. 关键词
        2. 停用词
      2. IK分词器
    7. 高亮展示 (搜索结果中关键词变色处理)
    8. 项目集成
      1. 后台集成方案:表格展示
      2. web端方案:搜索页面—>搜索结果展示页面【来自索引库】—>结果详情页【来自数据库】

补充参考资料

  1. 第三阶段补充课程 资料
  2. 博客 全文检索的原理 【了解】

全文检索

  1. 索引创建
  2. 查询过程
  3. 全文特点
  4. 代码环节
    1. 索引的创建
      1. 准备数据 将数据封装在 Document 对象 文档对象
      2. 将数据写出到 索引库 在写的同时Lucene会自动的做分词处理 并且 创建索引
        1. 索引库位置的定义
        2. 索引写出的参数的定义 版本 分词器
      3. 将文档对象交给 索引写出对象
      4. 提交到索引库
      5. 释放资源
    2. 通过关键词查询
      1. 获取搜索关键词
      2. 处理搜索关键词
        1. 需要定义 要查询哪些属性 使用什么分词器
        2. 需要保证 查询时的分词器 和 创建索引时的分词器 是同一个
      3. 通过索引查询对象 读取索引库 直接调用方法查询 得到结果集
      4. 处理结果集 得到 文档对象的id (位置信息)
      5. 通过文档对象的id 再次查询 得到原始的数据 (Document 对象 )
    3. 索引库的数据结构
      1. 两个区域:索引区和元数据区
      2. Field.Store.YES 和 Field.Store.NO 区别
    4. 封装工具类 【常规操作】
    5. 删除和修改
      1. 根据 id 删除
        1. IntField
        2. StringField
    6. 分词器
      1. 分词规则
        1. 关键词
        2. 停用词
      2. IK分词器
    7. 高亮展示 (搜索结果中关键词变色处理)
    8. 项目集成
      1. 后台集成方案:表格展示
      2. web端方案:搜索页面—>搜索结果展示页面【来自索引库】—>结果详情页【来自数据库】

补充参考资料

  1. 第三阶段补充课程 资料
  2. 博客 全文检索的原理 【了解】
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

明月清风,良宵美酒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值