全文搜索引擎Elasticsearch的基本原理及使用

全文搜索引擎的概念

全文搜索引擎是目前广泛应用的主流搜索引擎。它的工作原理是计算机索引程序通过扫描文章中的每一个词,对每一个词建立一个索引,指明该词在文章中出现的次数和位置,当用户查询时,检索程序就根据事先建立的索引进行查找,并将查找的结果反馈给用户的检索方式。这个过程类似于通过字典中的检索字表查字的过程。

常见的全文搜索引擎及对比

①Lucene

Lucene是一个Java全文搜索引擎,完全用Java编写。Lucene不是一个完整的应用程序,而是一个代码库和API,可以很容易地用于向应用程序添加搜索功能

优点:比较成熟的解决方案,具有活跃的社区,经过优化,可以支持10亿+的搜索
缺点:所有的扩展,分布式,可靠性等都需要自己实现;非实时,从建索引到可以搜索中间有一个时间延迟。

②solar

Solr是一个基于名为Lucene的Java库构建的开源搜索平台。它以用户友好的方式提供Apache Lucene的搜索功能。作为一个行业参与者近十年,它是一个成熟的产品,拥有强大而广泛的用户社区。它提供分布式索引,复制,负载平衡查询以及自动故障转移和恢复。如果它被正确部署然后管理得好,它就能够成为一个高度可靠,可扩展且容错的搜索引擎

优点:Solr有一个更大、更成熟的用户、开发和贡献者社区;支持多种文件的索引,如JSON,XML,CSV,PDF,HTML,WORD;不考虑建立索引,搜索速度更快。
缺点:建立索引耗时较长,实时搜索的效率不高

③Elasticsearch

Elasticsearch(elastic)是一个基于Apache Lucene库构建的RESTful搜索引擎,它提供了一个分布式,多租户能力的全文搜索引擎,具有HTTP Web界面(REST)和无架构JSON文档。分布式搜索引擎包括可以划分为分片的索引,并且每个分片可以具有多个副本。每个Elasticsearch节点都可以有一个或多个分片,其引擎也可以充当协调器,将操作委派给正确的分片。

优点:Elasticsearch是分布式的,可以实时分发;支持Lucene接近实时的搜索;处理多租户(multienancy)不需要特殊的配置,而slor则需要更多高级的配置
缺点:社区没有solar那么活跃;由于发布出来时间较短,较solar不稳定;不够自动,不适应当前新的index warmup API(索引预热)

Elasticsearch基本概念

①Index:索引,Elastic 数据管理的顶层单位,写入数据后经处理会生成倒排索引(Inverted Index)
②Document:文档,Index里面的单条数据记录
③Type:Document可以进行分组,Type就是这种分组,使用Type可以对Document进行过滤
④Cluster:集群,Elastic是一个分布式数据库,众多的节点组成了一个集群
⑤Node:单个elastic实例为一个节点,节点分为主节点跟分节点。当一个节点被选举成为主节点时,它将负责管理集群范围内的所有变更,例如增加、删除索引,或者增加、删除节点等,任何节点都有可能被选举成主节点
⑥分片:数据的容器,分为主分片跟副分片。一个主分片理论上可以存储Integer.MAX_VALUE - 128 个文档,索引建立时已经确定了主分片的数量。副分片作为主分片的拷贝,作为硬件故障时保护数据不丢失的冗余备份。分片可以分布在同一节点,但是为了做到数据备份跟负载均衡,一般会将不同分片分布在不同的节点上。

全文搜索引擎基本原理-倒排索引

当往Elasticsearch插入数据时,会建立对应的文档来存储数据,并且根据分词规则对数据进行拆分,然后建立不同的词条跟所在文档的倒排索引。当进行搜索时,会对搜索关键字进行拆分,并且匹配倒排索引中维护的词条,最终查找出对应的文档。
如现在存在两个文档,每个文档中的content包括以下内容:
1.本文作者是吃雪糕也放辣椒
2.读完本文给作者点个赞
然后根据分词规则将文本拆分成词条,并且建立倒排索引,如下所示:


倒排索引表.png

当输入“读完本文”关键字时,会匹配到以下结果:


匹配结果.png

从上面匹配结果来看,文档2的匹配度更加高。如果我们使用仅计算匹配词条数量的简单 相似性算法 ,那么,我们可以说,对于我们查询的相关性来讲,第二个文档比第一个文档更佳。

Elastic的简单使用

①环境的搭建

1.安装1.8版本及以上JDK

2.安装elastic,直接上官网下载解压即可,下载链接>https://www.elastic.co/cn/downloads/elasticsearch
解压完后,cd /安装目录/elasticsearch-6.5.4/bin
运行 elasticsearch.bat,可以看到elastic已经以9300的端口启动:

elasticsearch启动图.png

http端口可以在 /安装目录/elasticsearch-6.5.4/config/elasticsearch.yml中进行修改

3.安装IK分词器,下载链接>https://github.com/medcl/elasticsearch-analysis-ik/releases
解压在 /安装目录/elasticsearch-6.5.4/plugins即可

4.安装可视化界面kibana,下载链接>https://www.elastic.co/cn/downloads/kibana
下载完后直接解压即可,cd /安装目录/kibana-6.5.4-windows-x86_64/config,在kibana.yml可以设置elastic的ip及端口:

kibana.png

然后cd /安装目录/kibana-6.5.4-windows-x86_64/bi,进入kibana.bat启动

②SpringBoot整合Elasticsearch

1.导入elastic依赖

  
            org.springframework.boot
            spring-boot-starter-data-elasticsearch
        

2.配置节点

#application.properties
# 配置集群名称,名称写错会连不上服务器,默认elasticsearch
spring.data.elasticsearch.cluster-name=elasticsearch
# 配置集群节点
spring.data.elasticsearch.cluster-nodes=localhost:9300

#是否开启本地存储
spring.data.elasticsearch.repositories.enabled=true

3.建立模型Demo

@Data
@Document(indexName="shop")
public class Product {
    @Id
    private String id;

    /**
     * searchAnalyzer:分词器
     * type:字段类型
     * fielddata:预加载
     */
    @Field(analyzer="ik_smart",searchAnalyzer="ik_smart",type = FieldType.Text,fielddata = true)
    private String title;

    private Integer price;
    
    @Field(analyzer="ik_smart",searchAnalyzer="ik_smart",type = FieldType.Text,fielddata = true)
    private String intro;

    @Field(type= FieldType.Keyword)
    private String brand;
}

4.创建Repository

public interface EsRepository extends ElasticsearchRepository {
}

5.创建业务接口

/**
 * @author wangsj
 */
public interface EsProduct {
    /**
     * 添加数据
     * @param product 商品对象
     * @return 返回添加的商品对象
     */
    Product create(Product product);

    /**
     * 魔魂查询
     * @param keyword 名字
     * @return 商品集合
     */
    List search(String keyword);


    /**
     * 高亮查询
     * @param keyword 搜索关键字
     * @return AggregatedPage
     */
    AggregatedPage highLightSearch(String keyword);
}

6.业务接口实现类

public class EsProductImpl implements EsProduct {
    @Autowired
    private EsRepository esRepository;
    @Autowired
    private ElasticsearchTemplate template;
    @Override
    public Product create(Product product) {
        Product pro = esRepository.save(product);
        return pro;
    }
    @Override
    public List search(String keyword) {
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
        builder.withQuery(
                QueryBuilders.multiMatchQuery(keyword, "title", "intro")
        );
        builder.withPageable(PageRequest.of(0, 100, Sort.Direction.DESC, "brand"));
        Page search = esRepository.search(builder.build());
        List pros = search.getContent();
        return pros;
    }

    @Override
    public AggregatedPage highLightSearch(String keyword) {
        // Java与JSON互转的工具对象
        ObjectMapper mapper = new ObjectMapper();

        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
        // 设置查询哪个索引中的哪个类型
        builder.withIndices("shop");
        builder.withQuery(
                QueryBuilders.multiMatchQuery(keyword,
                        "title", "intro")
        );
        builder.withHighlightFields(
                new HighlightBuilder.Field("title")
                        .preTags("").postTags(""),
                new HighlightBuilder.Field("intro")
                        .preTags("").postTags("")
        );
        AggregatedPage page = template.queryForPage(builder.build(), Product.class,
                new SearchResultMapper(){
                    @Override
                    public  AggregatedPage mapResults(SearchResponse response, Class clazz, Pageable pageable) {
                        List list = new ArrayList<>();

                        for (SearchHit hit : response.getHits().getHits()) {
                            list.add(mapSearchHit(hit, clazz));
                        }
                        long total = response.getHits().totalHits;
                        return new AggregatedPageImpl<>(list, pageable, total);
                    }
                    @Override
                    public  T mapSearchHit(SearchHit searchHit, Class type) {
                        T t = null;
                        try {
                            t = mapper.readValue(searchHit.getSourceAsString(), type);
                            for (HighlightField field : searchHit.getHighlightFields().values()) {
                                // 替换需要高亮显示的字段,用到Apache的BeanUtils工具
                                BeanUtils.setProperty(t, field.getName(), field.getFragments()[0].string());
                            }

                        }catch (Exception e){
                            e.printStackTrace();
                            return null;
                        }
                        return t;
                    }
                });

        return page;
    }
}

7.实例测试
(1)新增数据

    @Test
    public void testCreate(){
        Product product = new Product();
        product.setBrand("三星");
        product.setIntro("性价比不高的手机");
        product.setTitle("贵手机");
        product.setId("3");
        esProduct.create(product);
    }

打开kibana,可以看到elastic中已经新建立了一条数据


kibana.png

(2)模糊查询

 @Test
    public void testSearch(){
        List pros = esProduct.search("甩");
        System.out.println(pros);
    }

输入搜索关键字为“甩”,运行结果:


模糊查询.png

(3)高亮查询

  @Test
    public void testHighLightSearch(){
        AggregatedPage page = esProduct.highLightSearch("甩");
        page.forEach(System.out::println);
    }

输入搜索关键字为“甩”,运行结果:


image.png

在页面直观的效果类似于往百度输入搜索关键字,然后返回结果会将关键字渲染成红色


高亮效果.png
③Elasticsearch进阶

elastic可以进行复杂的聚合运算,如桶聚合,还可以进行统计、排序、分页等等,进一步学习可以参考官方文档:>https://www.elastic.co/guide/en/elasticsearch/reference/6.0/search-aggregations.html

你可能感兴趣的:(全文搜索引擎Elasticsearch的基本原理及使用)