全文搜索引擎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依赖

  <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>

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<Product, String> {
}

5.创建业务接口

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

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


    /**
     * 高亮查询
     * @param keyword 搜索关键字
     * @return AggregatedPage
     */
    AggregatedPage<Product> 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<Product> 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<Product> search = esRepository.search(builder.build());
        List<Product> pros = search.getContent();
        return pros;
    }

    @Override
    public AggregatedPage<Product> 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("<span style='color:red'>").postTags("</span>"),
                new HighlightBuilder.Field("intro")
                        .preTags("<span style='color:red'>").postTags("</span>")
        );
        AggregatedPage<Product> page = template.queryForPage(builder.build(), Product.class,
                new SearchResultMapper(){
                    @Override
                    public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
                        List<T> 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> T mapSearchHit(SearchHit searchHit, Class<T> 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<Product> pros = esProduct.search("甩");
        System.out.println(pros);
    }

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


模糊查询.png

(3)高亮查询

  @Test
    public void testHighLightSearch(){
        AggregatedPage<Product> 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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容