【搜索引擎】使用层面:Lucene(ES和Solr都是基于Lucene)

一、前言

二、站内搜索or站外搜索,数据库or搜索库

Lucene定义(这个定义面试没用,面试能说出下面“面试语言组织”那句话,作为介绍Lucene的起手式就好了):
Lucene是apache软件基金会发布的一个开放源代码的全文检索引擎工具包,是一个全文检索引擎的架构,提供了完整的创建索引和查询索引,以及部分文本分析的引擎,Lucene的目的是为软件开发人员提供一个简单易用的工具包,以方便在目标系统中实现全文检索的功能,或者是以此为基础建立起完整的全文检索引擎,Lucene在全文检索领域是一个经典的祖先,现在很多检索引擎都是在其基础上创建的,比如ES和Solr。
面试语言组织:Lucene是全文搜索引擎,ES和Solr都是基于Lucene。

Lucene是根据关健字来搜索的文本搜索工具,只能在某个网站内部搜索文本内容,不能跨网站搜索

既然谈到了网站内部的搜索,那么我们就谈谈我们熟悉的百度、google那些搜索引擎又是基于什么搜索的呢….

在这里插入图片描述

问题1:浏览器点击“百度一下”,百度的服务器如何去全网查找信息?
标准答案:
步骤1:从浏览器发送post请求给百度服务器,请求体中带上搜索框文本,百度服务器接收这个post请求,仅仅一个网络请求,这个步骤很快;
步骤2:百度服务器使用网络爬虫去全网爬取信息,然后处理搜索结果,交了钱的排在前面,这个步骤就是站外搜索的核心算法,这个步骤比步骤1块慢,但是百度的服务器很快了,所以感觉不出来,还有的,如果是热词,人家不做全网爬取了,直接将索引库中的返回。
步骤3:请求处理完成,返回为浏览器,用户界面跳转,而且搜索列表出来了。
问题2:百度公司的内部服务器中索引库里面的数据是不是不变的?
标准答案:可以变化的。

对于上图的解释:
小结:baidu、google等搜索引擎其实是通过网络爬虫的程序来进行搜索的…,所以,Lucene又不是搜索引擎,仅仅是在网站内部进行文本的搜索,即只能站内搜索,同样的,基于Lucene实现的Solr和ES也都只能完成站内搜索(因为将数据库中的数据导入到索引库,只有自己网站的数据,只能站内搜索),站外搜索就是搜索引擎(如百度、谷歌)是通过网络爬虫决定的

问题:解释一下站内搜索和站外搜索(即全网搜索)?
标准答案:直接从数据源头上可以说清楚,搜索一定是对数据进行搜索,即搜索数据,具体的数据包括:文本、静态资源(文本自己存在数据库里面,图片音视频等静态资源路径(http网络路径和服务器存储路径)存放在数据库里面)
核心是数据来源
站内搜索定义:对于站内搜索,数据就在自己数据库里面,两种方式
第一,如果数据量不大,直接用 like+% 在数据库里面找就好了
第二,如果数据量大,在数据库里面找太慢了,将数据库中需要进行搜索的字段导入到索引库中,配置好中文分词器和英文分词器,然后就可以找索引库中找了,找到后拿着id到数据库中取该id对应的完整数据

问题(第一种和第二种为什么有速度差别):索引库最后找到后, 找到后拿着id到数据库中取该id对应的完整数据,还是到数据库中找,为什么不直接用like+%在数据库中找?
标准答案:速度,直接用like+%在数据库中找,不走索引,数据库很慢;先用索引库找到后, 找到后拿着id到数据库中取该id对应的完整数据,id如果是主键,自带聚集索引(聚集索引建表的时候自带,二级索引要自己新建),很快。而且,只有到索引库找到后,后面的操作需要的数据在索引库中没有,才带着id到数据库中找,如果索引库中有,就不用到数据库中找了。

第三,附加一种情况,ELK日志收集,对于logStash或者FileBeat(四种beat之一)收集的日志,存放到es中,r然后在es中找,很快,向日志这种数量大且实时性高且过时无价值的数据,放在es里面,当时使用,定期删除,不用来劳累数据库
站外搜索定义:对于站外搜索,数据不在自己数据库里面,要在全网找,数据量是非常大的,使用 网络爬虫 + 复杂的搜索算法,如百度和谷歌,了解就好,
小结:广义上的搜索引擎包括站内搜索引起和站外搜素引擎,狭义上的搜索引擎仅仅是指站外搜索引擎。。
小结:什么事搜索引擎?回答:面试问就直接把上面的站内搜索+站外搜索怼上去就好了,其实,从用户的角度看,能够完成搜索功能的都是搜索引擎,小网站直接 写个 like +% 的SQL语句,也可以叫搜索引擎,反正用户不懂。

站内搜索就是对我们自己制作的网站搜索,就是对我们自己的数据库搜索,可以直接使用myql的 like+% 实现,也可以将数据库中要搜索的字段导入到索引库,使用Lucene、Solr、ES全文搜索引擎来搜索。
站内搜素,用SQL搜索的缺点,Lucene的优点:
(1)磁盘文件搜索:SQL只能针对数据库的一个表或多个表中的数据搜索,不能直接针对硬盘上的文本搜索;但是Lucene可以对导入的数据(从数据库中导入、从磁盘文件中导入、从redis中导入),都可以搜索,可以对磁盘文件搜索,比如ELK日志收集与分析,LogStash读取各种中间件日志文件,输出到ES中,ES对自己存储的数据搜索。
(2)相关度排名:SQL没有相关度排名,无法对搜素出来的记录排名,Lucene可以对搜索结果排名
(3)关键字高亮:SQL搜索结果没有关健字高亮显示,但是Lucene通过导入相同的库或依赖,可以实现关键字高亮显示。
(4)部署在服务器上的占用空间和运行在服务器上的内存消耗:SQL需要数据库的支持,数据库本身需要内存开销较大,例如:Oracle,Lucene/ES/Solr作为一个中间件服务,占用磁盘空间比数据库小(因为只要导入lucene的相关jar包到web工程就好,所以,无需额外安装程序,索引库存储占用一点点磁盘空间),和运行时内存消耗比数据库小(因为嵌入到web工程中,所以不额外启动进程占用运行内存)。ps:笔者觉得这个不是特别大的优点,Lucene/ES/Solr找到后,然后后面的操作需要的数据在索引库中没有,还是要到数据库中找。
(5)SQL搜索因为不走索引较慢,尤其是数据库不在本地时,需要网络连接,超慢,例如:Oracle,Lucene/ES/Solr搜索块。ps:搜索速度是最大优点
(6)相同点:都可以使用java操作,java可以通过JDBC读写数据库数据,可以通过特定API读写索引库数据;demo中直接访问数据,项目中都是持久层访问数据。
(7)Luence的不足:Luence只能存放文本类型的小量数据,数据库可以任意类型,且大小不限。

三、Luence快速入门

Lucene存放的究竟是什么内容?
关于SQL,运行的时候是数据库中的内存,这些东西在硬盘中为DBF文件;
Lucene存放数据的地方我们通常称之为索引库,它们本质上是位于计算机的硬盘上是一系列的二进制压缩文件和一些控制文件,
索引库有两部份组成:
(1)原始记录
存入到索引库中的原始文本,例如:csdn is good website
(2)词汇表
按照一定的拆分策略(即分词器)将原始记录中的每个字符拆开后,存入一个供将来搜索的表
小结:Lucene存放数据的地方我们通常称之为索引库,索引库又分为两部分组成:原始记录和词汇表

3.1 原始记录和词汇表

当我们想要把数据存到索引库的时候,我们首先存入的是将数据存到原始记录上面去….

又由于我们给用户使用的时候,用户使用的是关键字来进行查询我们的具体记录。因此,我们需要把我们原始存进的数据进行拆分!将拆分出来的数据存进词汇表中。

词汇表就是类似于我们在学Oracle中的索引表,拆分的时候会给出对应的索引值。

一旦用户根据关键字来进行搜索,那么程序就先去查询词汇表中有没有该关键字,如果有该关键字就定位到原始记录表中,将符合条件的原始记录返回给用户查看。

我们查看以下的图方便理解:

在这里插入图片描述

到了这里,有人可能就会疑问:难道原始记录拆分的数据都是一个一个汉字进行拆分的吗??然后在词汇表中不就有很多的关键字了???

其实,我们在存到原始记录表中的时候,可以指定我们使用哪种算法来将数据拆分,存到词汇表中……我们的图是Lucene的标准分词算法,一个一个汉字进行拆分。我们可以使用别的分词算法,两个两个拆分或者其他的算法。

3.2 实践:将数据写入到索引库,读取索引库中的数据

3.2.1 导入依赖,配置Bean类

首先,我们来导入Lucene的必要开发包:
lucene-core-3.0.2.jar【Lucene核心】
lucene-analyzers-3.0.2.jar【分词器】
lucene-highlighter-3.0.2.jar【Lucene会将搜索出来的字,高亮显示,提示用户】
lucene-memory-3.0.2.jar【索引库优化策略】
创建User对象,User对象封装了数据….

public class User {
    
    


    private String id ;
    private String userName;
    private String sal;

    public User() {
    
    

    }
    public User(String id, String userName, String sal) {
    
    
        this.id = id;
        this.userName = userName;
        this.sal = sal;
    }
    public String getId() {
    
    
        return id;
    }

    public void setId(String id) {
    
    
        this.id = id;
    }

    public String getUserName() {
    
    
        return userName;
    }

    public void setUserName(String userName) {
    
    
        this.userName = userName;
    }

    public String getSal() {
    
    
        return sal;
    }

    public void setSal(String sal) {
    
    
        this.sal = sal;
    }
}

3.2.2 创建并写入数据到索引库

我们想要使用Lucene来查询出站内的数据,首先我们得要有个索引库吧!于是我们先创建索引库,将我们的数据存到索引库中。

创建索引库的步骤:
1)创建JavaBean对象
2)创建Docment对象
3)将JavaBean对象所有的属性值,均放到Document对象中去,属性名可以和JavaBean相同或不同
4)创建IndexWriter对象
5)将Document对象通过IndexWriter对象写入索引库中
6)关闭IndexWriter对象

@Test
public void createIndexDB() throws Exception {
    
    

    //把数据填充到JavaBean对象中
    User user = new User("1", "钟福成", "未来的程序员");

    //创建Document对象【导入的是Lucene包下的Document对象】
    Document document = new Document();

    //将JavaBean对象所有的属性值,均放到Document对象中去,属性名可以和JavaBean相同或不同


    /**
     * 向Document对象加入一个字段
     * 参数一:字段的关键字
     * 参数二:字符的值
     * 参数三:是否要存储到原始记录表中
     *      YES表示是
     *      NO表示否
     * 参数四:是否需要将存储的数据拆分到词汇表中
     *      ANALYZED表示拆分
     *      NOT_ANALYZED表示不拆分
     *
     * */
    document.add(new Field("id", user.getId(), Field.Store.YES, Field.Index.ANALYZED));
    document.add(new Field("userName", user.getUserName(), Field.Store.YES, Field.Index.ANALYZED));
    document.add(new Field("sal", user.getSal(), Field.Store.YES, Field.Index.ANALYZED));

    //创建IndexWriter对象
    //目录指定为E:/createIndexDB
    Directory directory = FSDirectory.open(new File("E:/createIndexDB"));

    //使用标准的分词算法对原始记录表进行拆分
    Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_30);

    //LIMITED默认是1W个
    IndexWriter.MaxFieldLength maxFieldLength = IndexWriter.MaxFieldLength.LIMITED;
    /**
     * IndexWriter将我们的document对象写到硬盘中
     *
     * 参数一:Directory d,写到硬盘中的目录路径是什么
     * 参数二:Analyzer a, 以何种算法来对document中的原始记录表数据进行拆分成词汇表
     * 参数三:MaxFieldLength mfl 最多将文本拆分出多少个词汇
     *
     * */
    IndexWriter indexWriter = new IndexWriter(directory, analyzer, maxFieldLength);

    //将Document对象通过IndexWriter对象写入索引库中
    indexWriter.addDocument(document);

    //关闭IndexWriter对象
    indexWriter.close();

}

程序执行完,我们就会在硬盘中见到我们的索引库。

那我们现在是不知道记录是否真真正正存储到索引库中的,因为我们看不见。索引库存放的数据放在cfs文件下,我们也是不能打开cfs文件的。

于是,我们现在用一个关键字,把索引库的数据读取。看看读取数据是否成功。

3.2.3 读取索引库中的数据

根据关键字查询索引库中的内容:

1)创建IndexSearcher对象
2)创建QueryParser对象
3)创建Query对象来封装关键字
4)用IndexSearcher对象去索引库中查询符合条件的前100条记录,不足100条记录的以实际为准
5)获取符合条件的编号
6)用indexSearcher对象去索引库中查询编号对应的Document对象
7)将Document对象中的所有属性取出,再封装回JavaBean对象中去,并加入到集合中保存,以备将之用

@Test
public void findIndexDB() throws Exception {
    
    

    /**
     * 参数一: IndexSearcher(Directory path)查询以xxx目录的索引库
     *
     * */
    Directory directory = FSDirectory.open(new File("E:/createIndexDB"));
    //创建IndexSearcher对象
    IndexSearcher indexSearcher = new IndexSearcher(directory);

    //创建QueryParser对象
    /**
     * 参数一: Version matchVersion 版本号【和上面是一样的】
     * 参数二:String f,【要查询的字段】
     * 参数三:Analyzer a【使用的拆词算法】
     * */
    Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_30);
    QueryParser queryParser = new QueryParser(Version.LUCENE_30, "userName", analyzer);

    //给出要查询的关键字
    String keyWords = "钟";

    //创建Query对象来封装关键字
    Query query = queryParser.parse(keyWords);

    //用IndexSearcher对象去索引库中查询符合条件的前100条记录,不足100条记录的以实际为准
    TopDocs topDocs = indexSearcher.search(query, 100);

    //获取符合条件的编号

    for (int i = 0; i < topDocs.scoreDocs.length; i++) {
    
    

        ScoreDoc scoreDoc = topDocs.scoreDocs[i];
        int no = scoreDoc.doc;
        //用indexSearcher对象去索引库中查询编号对应的Document对象
        Document document = indexSearcher.doc(no);

        //将Document对象中的所有属性取出,再封装回JavaBean对象中去
        String id = document.get("id");
        String userName = document.get("userName");
        String sal = document.get("sal");

        User user = new User(id, userName, sal);
        System.out.println(user);

    }

3.3 小结:Lucene索引库读写数据

Lucene索引库读写数据:
写操作两步骤
第一,将JavaBean对象封装到Document对象中;
第二,通过IndexWriter把document写入到索引库中。
读操作两步骤:
第一,当用户需要查询的时候,就使用IndexSearcher从索引库中读取数据,找到对应的Document对象;
第四,解析里边的内容,再封装到JavaBean对象中让我们使用。

在这里插入图片描述

四、底层:索引库底层如何优化自己性能的(重要)

索引库提供底层优化自己性能的方式,如合并文件、善用内存索引库,程序员调用函数就好了

4.1合并文件

我们把数据添加到索引库中的时候,每添加一次,都会帮我们自动创建一个cfs文件…

这样其实不好,因为如果数据量一大,我们的硬盘就有非常非常多的cfs文件了……其实索引库会帮我们自动合并文件的,默认是10个。

如果,我们想要修改默认的值,我们可以通过以下的代码修改:

//索引库优化
indexWriter.optimize();

//设置合并因子为3,每当有3个cfs文件,就合并
indexWriter.setMergeFactor(3);

4.2 善用内存索引库

我们的目前的程序是直接与文件进行操作,这样对IO的开销其实是比较大的。而且速度相对较慢….我们可以使用内存索引库来提高我们的读写效率…

内存索引库
优点:速度快,直接操作内存
缺点:程序员麻烦,
写入数据到索引库的时候,先将内存索引库是要到硬盘索引库中保存起来的;
从索引库读取数据的时候,先要把硬盘索引库的数据同步到内存索引库中去的。

Article article = new Article(1,"培训","传智是一家Java培训机构");
Document document = LuceneUtil.javabean2document(article);

Directory fsDirectory = FSDirectory.open(new File("E:/indexDBDBDBDBDBDBDBDB"));
Directory ramDirectory = new RAMDirectory(fsDirectory);

IndexWriter fsIndexWriter = new IndexWriter(fsDirectory,LuceneUtil.getAnalyzer(),true,LuceneUtil.getMaxFieldLength());
IndexWriter ramIndexWriter = new IndexWriter(ramDirectory,LuceneUtil.getAnalyzer(),LuceneUtil.getMaxFieldLength());

ramIndexWriter.addDocument(document);
ramIndexWriter.close();

fsIndexWriter.addIndexesNoOptimize(ramDirectory);
fsIndexWriter.close();

五、分词器

将数据存到索引库的时候,原始记录表中存放原始记录,词汇表中存放分词后的词汇

分词器: ** 采用一种算法,将中英文本中的字符拆分开来,形成词汇,以待用户输入关健字后搜索**

对于为什么要使用分词器,我们也明确地说过:由于用户不可能把我们的原始记录数据完完整整地记录下来,于是他们在搜索的时候,是通过关键字进行对原始记录表的查询….此时,我们就采用分词器来最大限度地匹配相关的数据。

5.1 理论:分词器

步骤1:按分词器拆分出词汇
步骤2:去除停用词和禁用词
步骤3:如果有英文,把英文字母转为小写,即搜索不分大小写

5.2 实践:代码中使用分词器

我们在选择分词算法的时候,我们会发现有非常非常多地分词器API,我们可以用以下代码来看看该分词器是怎么将数据分割的:

private static void testAnalyzer(Analyzer analyzer, String text) throws Exception {
    
    
    System.out.println("当前使用的分词器:" + analyzer.getClass());
    TokenStream tokenStream = analyzer.tokenStream("content",new StringReader(text));
    tokenStream.addAttribute(TermAttribute.class);
    while (tokenStream.incrementToken()) {
    
    
        TermAttribute termAttribute = tokenStream.getAttribute(TermAttribute.class);
        System.out.println(termAttribute.term());
    }
}

在实验完之后,我们就可以选择恰当的分词算法了….

5.3 中文分词器:IKAnalyzer分词器(一款中文分词器)

步骤1:导入对应的jar包,IKAnalyzer3.2.0Stable.jar
步骤2:将IKAnalyzer.cfg.xml和stopword.dic和xxx.dic文件复制到MyEclipse的src目录下,再进行配置,在配置时,首行需要一个空行

六、对搜索结果进行处理

6.1 搜索结果高亮

金手指:设置内容高亮,两句就好
//设置内容高亮
String highlighterContent = highlighter.getBestFragment(LuceneUtil.getAnalyzer(),“content”,document.get(“content”));
document.getField(“content”).setValue(highlighterContent);

我们在使用SQL时,搜索出来的数据是没有高亮的…而我们使用Lucene,搜索出来的内容我们可以设置关键字为高亮…这样一来就更加注重用户体验了!

    String keywords = "csdn";
    List<Article> articleList = new ArrayList<Article>();
    QueryParser queryParser = new QueryParser(LuceneUtil.getVersion(),"content",LuceneUtil.getAnalyzer());
    Query query = queryParser.parse(keywords);
    IndexSearcher indexSearcher = new IndexSearcher(LuceneUtil.getDirectory());
    TopDocs topDocs = indexSearcher.search(query,1000000);

    //设置关键字高亮
    Formatter formatter = new SimpleHTMLFormatter("<font color='red'>","</font>");
    Scorer scorer = new QueryScorer(query);
    Highlighter highlighter = new Highlighter(formatter,scorer);

    for(int i=0;i<topDocs.scoreDocs.length;i++){
    
    
        ScoreDoc scoreDoc = topDocs.scoreDocs[i];
        int no = scoreDoc.doc;
        Document document = indexSearcher.doc(no);

        //设置内容高亮
        String highlighterContent = highlighter.getBestFragment(LuceneUtil.getAnalyzer(),"content",document.get("content"));
        document.getField("content").setValue(highlighterContent);

        Article article = (Article) LuceneUtil.document2javabean(document,Article.class);
        articleList.add(article);
    }
    for(Article article : articleList){
    
    
        System.out.println(article);
    }
}

6.2 搜索结果摘要

搜素结果摘要
//设置摘要
Fragmenter fragmenter = new SimpleFragmenter(4);
highlighter.setTextFragmenter(fragmenter);

如果我们搜索出来的文章内容太大了,而我们只想显示部分的内容,那么我们可以对其进行摘要…

值得注意的是:搜索结果摘要需要与设置高亮一起使用

String keywords = "csdn";
        List<Article> articleList = new ArrayList<Article>();
        QueryParser queryParser = new QueryParser(LuceneUtil.getVersion(),"content",LuceneUtil.getAnalyzer());
        Query query = queryParser.parse(keywords);
        IndexSearcher indexSearcher = new IndexSearcher(LuceneUtil.getDirectory());
        TopDocs topDocs = indexSearcher.search(query,1000000);

        Formatter formatter = new SimpleHTMLFormatter("<font color='red'>","</font>");
        Scorer scorer = new QueryScorer(query);
        Highlighter highlighter = new Highlighter(formatter,scorer);

        //设置摘要
        Fragmenter fragmenter  = new SimpleFragmenter(4);
        highlighter.setTextFragmenter(fragmenter);

        for(int i=0;i<topDocs.scoreDocs.length;i++){
    
    
            ScoreDoc scoreDoc = topDocs.scoreDocs[i];
            int no = scoreDoc.doc;
            Document document = indexSearcher.doc(no);

            String highlighterContent = highlighter.getBestFragment(LuceneUtil.getAnalyzer(),"content",document.get("content"));
            document.getField("content").setValue(highlighterContent);

            Article article = (Article) LuceneUtil.document2javabean(document,Article.class);
            articleList.add(article);
        }
        for(Article article : articleList){
    
    
            System.out.println(article);
        }
    }

6.3 搜索结果排序

使用不同的搜索引擎来搜索相同的内容。他们首页的排行顺序也会不同,这就是它们内部用了搜索结果排序(比如百度的竞价排名)

设置相关度得分来使不同的结果对其进行排序

IndexWriter indexWriter = new IndexWriter(LuceneUtil.getDirectory(),LuceneUtil.getAnalyzer(),LuceneUtil.getMaxFieldLength());
//为结果设置得分
document.setBoost(20F);
indexWriter.addDocument(document);
indexWriter.close();

按单个字段排序

//true表示降序
Sort sort = new Sort(new SortField("id",SortField.INT,true));
TopDocs topDocs = indexSearcher.search(query,null,1000000,sort);

按多个字段排序:在多字段排序中,只有第一个字段排序结果相同时,第二个字段排序才有作用,提倡用数值型排序

Sort sort = new Sort(new SortField("count",SortField.INT,true),new SortField("id",SortField.INT,true));
TopDocs topDocs = indexSearcher.search(query,null,1000000,sort);

6.4 条件搜索

在我们的例子中,我们使用的是根据一个关键字来对某个字段的内容进行搜索。语法类似于下面:

QueryParser queryParser = new QueryParser(LuceneUtil.getVersion(),"content",LuceneUtil.getAnalyzer());

其实,我们也可以使用关键字来对多个字段进行搜索,也就是多条件搜索。我们实际中常常用到的是多条件搜索,多条件搜索可以使用我们最大限度匹配对应的数据!

QueryParser queryParser = new MultiFieldQueryParser(LuceneUtil.getVersion(),new String[]{
    
    "content","title"},LuceneUtil.getAnalyzer());

七、面试金手指

7.1 Lucene定义,站内搜索or站外搜索,数据库or搜索库

面试语言组织:Lucene是全文搜索引擎,ES和Solr都是基于Lucene。

问题:站内搜索or站外搜索
标准答案:直接从数据源头上可以说清楚,搜索一定是对数据进行搜索,即搜索数据,具体的数据包括:文本、静态资源(文本自己存在数据库里面,图片音视频等静态资源路径(http网络路径和服务器存储路径)存放在数据库里面)
核心是数据来源
站内搜索定义:对于站内搜索,数据就在自己数据库里面,两种方式
第一,如果数据量不大,直接用 like+% 在数据库里面找就好了
第二,如果数据量大,在数据库里面找太慢了,将数据库中需要进行搜索的字段导入到索引库中,配置好中文分词器和英文分词器,然后就可以找索引库中找了,找到后拿着id到数据库中取该id对应的完整数据

问题(第一种和第二种为什么有速度差别):索引库最后找到后, 找到后拿着id到数据库中取该id对应的完整数据,还是到数据库中找,为什么不直接用like+%在数据库中找?
标准答案:速度,直接用like+%在数据库中找,不走索引,数据库很慢;先用索引库找到后, 找到后拿着id到数据库中取该id对应的完整数据,id如果是主键,自带聚集索引(聚集索引建表的时候自带,二级索引要自己新建),很快。而且,只有到索引库找到后,后面的操作需要的数据在索引库中没有,才带着id到数据库中找,如果索引库中有,就不用到数据库中找了。

第三,附加一种情况,ELK日志收集,对于logStash或者FileBeat(四种beat之一)收集的日志,存放到es中,r然后在es中找,很快,向日志这种数量大且实时性高且过时无价值的数据,放在es里面,当时使用,定期删除,不用来劳累数据库
站外搜索定义:对于站外搜索,数据不在自己数据库里面,要在全网找,数据量是非常大的,使用 网络爬虫 + 复杂的搜索算法,如百度和谷歌,了解就好,
小结:广义上的搜索引擎包括站内搜索引起和站外搜素引擎,狭义上的搜索引擎仅仅是指站外搜索引擎。。
小结:什么事搜索引擎?回答:面试问就直接把上面的站内搜索+站外搜索怼上去就好了,其实,从用户的角度看,能够完成搜索功能的都是搜索引擎,小网站直接 写个 like +% 的SQL语句,也可以叫搜索引擎,反正用户不懂。

问题:数据库or搜索库
回答:站内搜索就是对我们自己制作的网站搜索,就是对我们自己的数据库搜索,可以直接使用myql的 like+% 实现,也可以将数据库中要搜索的字段导入到索引库,使用Lucene、Solr、ES全文搜索引擎来搜索。
站内搜素,用SQL搜索的缺点,Lucene的优点:
(1)磁盘文件搜索:SQL只能针对数据库的一个表或多个表中的数据搜索,不能直接针对硬盘上的文本搜索;但是Lucene可以对导入的数据(从数据库中导入、从磁盘文件中导入、从redis中导入),都可以搜索,可以对磁盘文件搜索,比如ELK日志收集与分析,LogStash读取各种中间件日志文件,输出到ES中,ES对自己存储的数据搜索。
(2)相关度排名:SQL没有相关度排名,无法对搜素出来的记录排名,Lucene可以对搜索结果排名
(3)关键字高亮:SQL搜索结果没有关健字高亮显示,但是Lucene通过导入相同的库或依赖,可以实现关键字高亮显示。
(4)部署在服务器上的占用空间和运行在服务器上的内存消耗:SQL需要数据库的支持,数据库本身需要内存开销较大,例如:Oracle,Lucene/ES/Solr作为一个中间件服务,占用磁盘空间比数据库小(因为只要导入lucene的相关jar包到web工程就好,所以,无需额外安装程序,索引库存储占用一点点磁盘空间),和运行时内存消耗比数据库小(因为嵌入到web工程中,所以不额外启动进程占用运行内存)。ps:笔者觉得这个不是特别大的优点,Lucene/ES/Solr找到后,然后后面的操作需要的数据在索引库中没有,还是要到数据库中找。
(5)SQL搜索因为不走索引较慢,尤其是数据库不在本地时,需要网络连接,超慢,例如:Oracle,Lucene/ES/Solr搜索块。ps:搜索速度是最大优点
(6)相同点:都可以使用java操作,java可以通过JDBC读写数据库数据,可以通过特定API读写索引库数据;demo中直接访问数据,项目中都是持久层访问数据。
(7)Luence的不足:Luence只能存放文本类型的小量数据,数据库可以任意类型,且大小不限。

7.2 索引库结构 + 索引库读写数据流程

Lucene中存的就是一系列的二进制压缩文件和一些控制文件,这些内容统称为索引库,索引库又分了两个部分:
原始记录、词汇表

Lucene索引库读写数据:将JavaBean对象封装到Document对象中,然后通过IndexWriter把document写入到索引库中。当用户需要查询的时候,就使用IndexSearcher从索引库中读取数据,找到对应的Document对象,从而解析里边的内容,再封装到JavaBean对象中让我们使用。
在这里插入图片描述

7.3 合并文件 + 设置内存索引库 + 分词器 + 结果高亮 + 结果摘要 + 结果排序 + 条件搜索

索引库两种优化方式:合并文件、设置内存索引库
Lucene分词器步骤:
步骤1:按分词器拆分出词汇
步骤2:去除停用词和禁用词
步骤3:如果有英文,把英文字母转为小写,即搜索不分大小写
一款中文分词器IKAnalyzer
查询结果四种处理:结果高亮、结果摘要、结果排序、条件搜索

八、小结

Lucene(ES和Solr都是基于Lucene),完成了。

天天打码,天天进步!!!

猜你喜欢

转载自blog.csdn.net/qq_36963950/article/details/108952802
今日推荐