你想要拥有自己的搜索引擎吗?

时间:2022-07-24
本文章向大家介绍你想要拥有自己的搜索引擎吗?,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
前言:

在现代开发的应用系统中,无论是常规的web应用,还是近几年兴起的app应用,或者是风头正热的大数据应用,都离不开搜索这一功能,搜索也是在应用系统中使用频率最高的一个功能,比如普通的商城系统中的商品搜索或者一些资源的站内搜索等。

大多数的应用系统的搜索都是针对数据库的搜索,比如说对某个表做一个按名称或针对某些字段的描述做模糊查询。更高级一点的是可以对搜索关键字进行分词,并且专门建一个搜索词库表,这种做法需要对搜索词进行拆解然后幂集组合并与目标表的ID关联,搜索字与词库表的字以完全匹配的方式查询并找到目标表的ID,这种搜索的解决方案需要拆解相关数据库表建立索引,相对比较麻烦。

所以,今天我们就来聊聊近两年兴起的分布式搜索引擎技术——Elasticsearch。

1.什么是搜索?

百度、Google:我们想寻找一个我们喜欢的小说、电影或者新闻等就可以去百度或者Google搜索一下。 互联网搜索:电商网站搜索商品,招聘网站搜索工作岗位,租房网站搜索房屋信息等。 企业应用系统的搜索:企业员工搜索,会议搜索,公文搜索等。

2.Elasticsearch是如何产生的?

1. 思考:百亿、千亿级海量数据是如何搜索的?

当系统数据量达到亿以上量级的时候,我们系统架构通常会从哪些角度去思考解决方案呢?

1)用什么数据库好?(mysql、db2、oracle、sqlserver、hbase等) 2)如何解决高并发和单点故障;(缓存、消息队列、系统拆分、读写分离、分库分表等) 3)如何保证数据库的高可用性和数据安全性;(冷备份、热备份、异地多活等) 4)如何解决数据检索效率难题;(数据库代理中间件:mysql-proxy、Cobar、mycat等;) 5)如何解决统计分析问题;(离线统计、近实时统计等)

2. 关系型数据库的解决方案

对于像mysql这样的传统关系型数据库,在以往的项目中,我们使用了以下架构方案去优化数据库的查询和写入瓶颈:

1) 通过主从备份来保证数据不会丢失,从而解决数据安全性问题; 2) 通过数据库代理中间件对数据库进行心跳监测,解决单点故障问题; 3) 通过消息队列对同一时段的大量请求进行削峰处理,解决高并发问题; 4) 通过数据库代理中间件将查询语句分发到各个slave节点进行查询,并汇总结果,提高查询效率; 5) 对于某些不需要实时统计的场景,如统计前一天的XX数据,通过离线计算并存储到缓存中,解决某些场景的统计问题;

问题:上述方案可以优化一部分搜索性能的问题,但是由于关系型数据库本身的特性,上述方案实施起来并不是一件容易的事,涉及到很多的组件,然而一个系统多一个组件,要保证其系统的稳定性的难度就会成几何指数增加,而且就算目前解决的问题,以后数据量再上升到一个量级时,此方案如何进一步扩展又是一份挑战。

3. 非关系型数据库的解决方案 对于像mongodb这样的nosql数据库,在以往的项目中,我们使用了以下架构方案去解决数据库的查询和写入瓶颈:

1)通过副本备份保证数据安全性; 2)通过节点竞选机制解决单点问题; 3)先从配置库检索分片信息,然后将请求分发到各个节点,最后由路由节点合并汇总结果

问题:mongodb这样的nosql数据库对于简单单表查询的效率是毋庸置疑的,但是它并不能很容易的做到复杂的多表聚合查询,比如涉及到多个表的的关联查询就会显得有些力不从心。

4. 如果我们把数据全部放到内存会怎么样? 假设我们现在有1PB的数据,按照每个节点128G内存来计算,在内存完全装满数据的情况下,我们一共需要的机器数量是:1PB=1024T=1048576G,节点数=1048576/128 =8192个,再考虑到数据的备份问题,往往实际场景下我们需要的机器会超过2W台,这样巨大的成本决定了其是不现实的!

所以,从前面讨论的情况来看,无论是把数据存到传统的基于磁盘的数据库中,还是把数据放到内存中,都无法完全解决大数据量下的搜索问题,根据这一情况,让我们从源头来思考一下:

1)数据和索引需要分离; 2)数据量过大时,数据需要压缩; 3)数据存储需要有序; 4)到达瓶颈时,要能够快捷、方便、无感知的进行水平拓展;

由此我们就引出了当下十分流行的分布式搜索引擎技术:Elasticsearch。

3.Elasticsearch基础

1. Elasticsearch是什么?

Elasticsearch是一个基于Apache Lucene(TM)的开源的高扩展的分布式搜索引擎 ,它可以近乎实时的存储、检索数据;本身扩展性很好,可以很轻松的扩展到上百台服务器,处理PB级别的数据。

2. Lucene是什么?

Lucene是一套用于全文检索和搜寻的开源程式库,由Apache软件基金会支持和提供。Lucene提供了一个简单却强大的应用程式接口,能够做全文索引和搜寻。在Java开发环境里Lucene是一个成熟的免费开源工具。就其本身而言,Lucene是当前以及最近几年最受欢迎的免费Java信息检索程序库。——《百度百科》 简单来说,Lucene就是一个jar包,里面包含了一系列的和搜索相关的方法,java工程只需要引入Lucene相关的jar包,就可以进行开发。

3. Lucene与Elasticsearch之间的关系

1)lucene,最先进、功能最强大的搜索库,直接基于lucene开发,非常复杂,api复杂(实现一些简单的功能,写大量的java代码),需要深入理解原理(各种索引结构) 。

2)elasticsearch,基于lucene,隐藏其开发的复杂性,提供简单易用的restful api接口、java api接口(还有其他语言的api接口) ,从而使我们能够更方便的实现搜索功能。

3) elasticsearch不仅仅是Lucene和全文搜索引擎 ,它还具有以下特点:

分布式的实时文件存储,每个字段都被索引并可被搜索 实时分析的分布式搜索引擎 可以扩展到上百台服务器,处理PB级结构化或非结构化数据

4. Elasticsearch 核心概念

1) 近实时(NRT) Elasticsearch是一个接近实时的搜索平台。这意味着,往索引插入一个文档直到这个文档能够被搜索到有一个轻微的延迟(通常是1秒)。

2) 集群(cluster) 集群由一个或多个节点组成,当有多个节点时,其中有一个为主节点,这个主节点是可以通过选举产生的,主从节点是对于集群内部来说的。es的一个概念就是去中心化,字面上理解就是无中心节点,这是对于集群外部来说的,因为从外部来看es集群,在逻辑上是个整体,你与任何一个节点的通信和与整个es集群通信是等价的。

3) 节点(node) 运行了单个实例的ES主机称为节点,它是集群的一个成员,可以存储数据、参与集群索引及搜索操作。类似于集群,节点靠其名称进行标识,默认为启动时自动生成的随机Marvel字符名称。用户可以按需要自定义任何希望使用的名称,但出于管理的目的,此名称应该尽可能有较好的识别性。节点通过为其配置的ES集群名称确定其所要加入的集群。

4)分片(shards) 代表索引分片,es可以把一个完整的索引分成多个分片(shards),这样的好处可以横向扩展,存储更多数据,让搜索和分析等操作分布到多台服务器上去执行,提升吞吐量和性能。分片的数量只能在索引创建前指定,并且索引创建后不能更改。

5)副本(replica) 任何一个服务器随时可能故障或宕机,此时shard可能就会丢失,因此可以为每个shard创建多个replica副本。replica可以在shard故障时提供备用服务,保证数据不丢失,多个replica还可以提升搜索操作的吞吐量和性能。primary shard(建立索引时一次设置,不能修改,默认5个),replica shard(随时修改数量,默认1个),默认每个索引10个shard,5个primary shard,5个replica shard,最小的高可用配置,是2台服务器。

6)索引 (index) 包含一堆有相似结构的文档数据,比如可以有一个用户信息索引,商品信息索引,订单信息索引等,一个索引由一个名字来标识(必须全部是小写字母的),并且当我们要对对应于这个索引中的文档进行索引、搜索、更新和删除的时候,都要使用到这个名字。索引类似于关系型数据库中Database的概念。在一个集群中,如果你想,可以定义任意多的索引。

7)类型(type) 每个索引里都可以有一个或多个type,type是index中的一个逻辑数据分类,一个type下的document,都有相同的field,比如博客系统,有一个索引,可以定义用户数据type,博客数据type,评论数据type等。类型类似于关系型数据库中Table的概念。

8)文档(document) document是es中的最小数据单元,一个document可以是一条客户数据,一条商品分类数据,一条订单数据,通常用JSON数据结构表示,每个index下的type中,都可以去存储多个document。一个document里面有多个field,每个field就是一个数据字段。

举例:

商品index,里面存放了所有的商品数据,商品document

但是商品分很多种类,每个种类的document的field可能不太一样,比如说电器商品,可能还包含一些诸如售后时间范围这样的特殊field;生鲜商品,还包含一些诸如生鲜保质期之类的特殊field

type,日化商品type,电器商品type,生鲜商品type

日化商品type:product_id,product_name,product_desc,category_id,category_name;

电器商品type:product_id,product_name,product_desc,category_id,category_name,service_period;

生鲜商品type:product_id,product_name,product_desc,category_id,category_name,eat_period;

每一个type里面,都会包含一堆document

{ "product_id": "2", "product_name": "长虹电视机", "product_desc": "4k高清", "category_id": "3", "category_name": "电器", "service_period": "1年"}

{ "product_id": "3", "product_name": "基围虾", "product_desc": "纯天然,冰岛产", "category_id": "4", "category_name": "生鲜", "eat_period": "7天"}

5. elasticsearch核心概念 vs. 数据库核心概念

6. Elasticsearch的功能

1)分布式的搜索引擎和数据分析引擎

搜索:百度,网站的站内搜索,IT系统的检索; 数据分析:电商网站,最近7天牙膏这种商品销量排名前10的商家有哪些;新闻网站,最近1个月访问量排名前3的新闻版块是哪些;

2)全文检索,结构化检索,数据分析

全文检索:我想搜索商品名称包含洗发水的商品,select * from products where product_name like "%洗发水%";

结构化检索:我想搜索商品分类为粮油用品的商品都有哪些,select * from products where category_type='粮油用品'; 部分匹配、自动完成、搜索纠错、搜索推荐等; 数据分析:我们分析每一个商品分类下有多少个商品,select category_type,count(*) from products group by category_type;

3)对海量数据进行近实时的处理 分布式:ES自动可以将海量数据分散到多台服务器上去存储和检索; 海量数据的处理:分布式以后,就可以采用大量的服务器去存储和检索数据,自然而然就可以实现海量数据的处理了; 近实时:检索个数据要花费1小时(这就不叫近实时,叫离线批处理);在秒级别对数据进行搜索和分析; 跟分布式/海量数据相反的:lucene,单机应用,只能在单台服务器上使用,最多只能处理单台服务器可以处理的数据量。

7. Elasticsearch的特点

1)可以作为一个大型分布式集群(数百台服务器)技术,处理PB级数据,服务大公司;也可以运行在单机上,服务小公司

2)Elasticsearch不是什么新技术,主要是将全文检索、数据分析以及分布式技术,合并在了一起,才形成了独一无二的ES;lucene(全文检索)+数据分析软件+分布式数据库(mycat)

3)对用户而言,是开箱即用的,非常简单,作为中小型的应用,直接3分钟部署一下ES,就可以作为生产环境的系统来使用了,数据量不大,操作不是太复杂

4)数据库的功能面对很多领域是不够用的(事务,还有各种联机事务型的操作);特殊的功能,比如全文检索,同义词处理,相关度排名,复杂数据分析,海量数据的近实时处理;Elasticsearch作为传统数据库的一个补充,提供了数据库所不不能提供的很多功能

8. Elasticsearch的适用场景

国外

1)维基百科,类似百度百科,牙膏,牙膏的维基百科,全文检索,高亮,搜索推荐

2)The Guardian(国外新闻网站),类似搜狐新闻,用户行为日志(点击,浏览,收藏,评论)+社交网络数据(对某某新闻的相关看法),数据分析,给到每篇新闻文章的作者,让他知道他的文章的公众反馈(好,坏,热门,垃圾,鄙视,崇拜)

3)Stack Overflow(国外的程序异常讨论论坛),IT问题,程序的报错,提交上去,有人会跟你讨论和回答,全文检索,搜索相关问题和答案,程序报错了,就会将报错信息粘贴到里面去,搜索有没有对应的答案

4)GitHub(开源代码管理),搜索上千亿行代码

5)电商网站,检索商品

6)日志数据分析,logstash采集日志,ES进行复杂的数据分析(ELK技术,elasticsearch+logstash+kibana)

7)商品价格监控网站,用户设定某商品的价格阈值,当低于该阈值的时候,发送通知消息给用户,比如说订阅牙膏的监控,如果高露洁牙膏的家庭套装低于50块钱,就通知我,我就去买

8)BI系统,商业智能,Business Intelligence。比如说有个大型商场集团,BI,分析一下某某区域最近3年的用户消费金额的趋势以及用户群体的组成构成,产出相关的数张报表,XX区,最近3年,每年消费金额呈现100%的增长,而且用户群体85%是高级白领,开一个新商场。ES执行数据分析和挖掘,Kibana进行数据可视化

国内

站内搜索(电商,招聘,门户,等等),IT系统搜索(OA,CRM,ERP,等等),数据分析(ES热门的一个使用场景)等

4.Elasticsearch倒排索引

上面一小段我们讨论了Elasticsearch的一些基本信息,这一小段我们来聊一聊Elasticsearch的倒排索引。

倒排索引(Inverted Index)也叫反向索引,有反向索引必有正向索引。通俗地来讲,正向索引是通过key寻找value,反向索引则是通过value寻找key。

我们先来看一下es是如何插入一条索引数据的:

curl -X PUT "localhost:9200/user/_doc/1" -H 'Content-Type: application/json' -d'
{
    "name" : "mary",
    "gender" : 0,
    "age" : 25
}

(左右滑动查看全部代码)

就是直接PUT一个JSON的对象,这个对象有多个字段,在插入这些数据到索引的同时,Elasticsearch还为这些字段建立索引——倒排索引,Elasticsearch最核心功能是搜索 ,建立倒排索引,就是为了提高搜索的性能。

那么倒排索引是什么样子的呢?

首先我们来搞清楚几个概念:

1)Term(单词):一段文本经过分析器分析以后就会输出一串单词,这一个一个的就叫做Term(直译为:单词) ;

2)Term Dictionary(单词字典):顾名思义,它里面维护的是Term,可以理解为Term的集合;

3)Term Index(单词索引):为了更快的找到某个单词,我们为单词建立索引 ;

4)Posting List(倒排列表):倒排列表记录了出现过某个单词的所有文档的文档列表及单词在该文档中出现的位置信息,每条记录称为一个倒排项(Posting)。根据倒排列表,即可获知哪些文档包含某个单词 ;

类比我们的新华词典,那么Term就相当于词语,Term Dictionary相当于汉语词典本身,Term Index相当于词典的目录索引。

下面再来举个例子看一看:

假设有个user索引,它有四个字段:分别是name,gender,age,address。大概是下面这个样子,跟关系型数据库一样 :

对于这张表中的数据,Elasticsearch建立的索引大致如下:

name字段:

age字段:

gender字段:

address字段:

Elasticsearch分别为每个字段都建立了一个倒排索引。比如,在上面“丽丽”、“北京市”、18 这些都是Term,而[2,3]就是Posting List。Posting list就是一个数组,存储了所有符合某个Term的文档ID;

在倒排索引中,通过Term索引可以找到Term在Term Dictionary中的位置,进而找到Posting List,有了倒排列表就可以根据ID找到文档了 ;

如果类比数据库的数据存储引擎的话:Term Index相当于索引文件,Term Dictionary相当于数据文件);

从最开始的那张图中,我们将倒排索引分成了三步,实际上我们可以把Term Index和Term Dictionary看成一步,就是找term;因此我们也可以这样理解倒排索引:通过单词找到对应的倒排列表,根据倒排列表中的倒排项进而可以找到文档记录。

5.Springboot整合Elasticsearch

上面给大家介绍了Elasticsearch相关的一些基本知识,由于篇幅有限,关于Elasticsearch的安装和基本使用方法请大家去阅读官网的资料,这里就不再赘述:

https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html

话不多说让我们来看看如何将Elasticsearch整合到一个springboot项目中:

1. 集成方式

Spring Boot中集成Elasticsearch有4种方式:

1)REST Client 2)Jest 3)Spring Data Elasticsearch Template 4)Spring Data Elasticsearch Repositories

本文用后面两种方式来分别连接并操作Elasticsearch

2. 环境与配置

服务端:elasticsearch-6.2.2 1台 服务端配置文件:elasticsearch.yml


cluster.name: springboot-es-application
network.host: 192.168.1.100
http.port: 9200

(左右滑动查看全部代码)

/etc/security/limits.conf


* soft nofile 65536
* hard nofile 65536

/etc/sysctl.conf


vm.max_map_count=262144

3. 版本

springboot版本:2.1.6.RELEASE elasticsearch版本:6.2.2

4. maven依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.primeton.example</groupId>
    <artifactId>primeton-elasticsearch-example</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>primeton-elasticsearch-example</name>
    <description></description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>

        <elasticsearch.version>6.2.2</elasticsearch.version>
        <spring.data.elasticsearch.version>3.1.0.RELEASE</spring.data.elasticsearch.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.elasticsearch</groupId>
            <artifactId>elasticsearch</artifactId>
            <version>${elasticsearch.version}</version>
        </dependency>
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>transport</artifactId>
            <version>${elasticsearch.version}</version>
        </dependency>
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-client</artifactId>
            <version>${elasticsearch.version}</version>
        </dependency>
        <dependency>
            <groupId>org.elasticsearch.plugin</groupId>
            <artifactId>transport-netty4-client</artifactId>
            <version>${elasticsearch.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-elasticsearch</artifactId>
            <version>${spring.data.elasticsearch.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

(左右滑动查看全部代码)

5. application.properties


spring.data.elasticsearch.cluster-name=springboot-es-application
spring.data.elasticsearch.cluster-nodes=192.168.1.100:9300

(左右滑动查看全部代码)

看到这里大家也许会感到疑惑,前面的配置文件端口号明明写的是9200,这里为了要写成9300呢?

因为:

9200端口是通过http协议连接es使用的端口; 9300端口是使用tcp客户端连接使用的端口;

因此咱们的springboot程序作为客户端想要连接elasticsearch就要使用9300端口;

6. 实战代码

1. 使用Spring Data Elasticsearch Repositories操作Elasticsearch

首先定义一个商品实体类entity


package com.primeton.example.entity;

import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;

import java.io.Serializable;

@Data
@Document(indexName = "product")
public class Product implements Serializable {

    @Id
    private String productId;

    private String name;

    private String category;

    private Integer price;

    private String brand;

    private Integer stock;

}

(左右滑动查看全部代码)

这里我们创建了一个Product的entity,表示商品的实体类。我们可以看到在Document注解中,只指定了indexName,并没有指定type,在elasticsearch6.x版本中,不建议使用type,而且在7.X版本中将会彻底废弃type。这里,一个Product就代表一个商品,同时也代表了一条索引记录。

这里类比关系型数据库的话,一个索引(Index)就相当于一张表,一个文档(Document)就相当于一条记录。

然后我们需要自己定义一个接口,并继承ElasticsearchRepository。


package com.primeton.example.dao;

import com.primeton.example.entity.Product;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface ProductRepository extends ElasticsearchRepository<Product, String> {

}

(左右滑动查看全部代码)

这里的Repository相当于我们常说的DAO层。

接下来定义service接口


package com.primeton.example.service;

import com.primeton.example.entity.Product;
import org.springframework.data.domain.Page;

import java.util.List;

public interface ProductService {

    long count();

    Product save(Product product);

    void delete(Product product);

    Iterable<Product> getAll();

    List<Product> getByName(String name);

    Page<Product> pageQuery(Integer pageNo, Integer pageSize, String kw);

}

(左右滑动查看全部代码)

定义service实现类


package com.primeton.example.service.impl;

import com.primeton.example.entity.Product;
import com.primeton.example.dao.ProductRepository;
import com.primeton.example.service.ProductService;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
public class ProductServiceImpl implements ProductService {

    @Autowired
    private ProductRepository ProductRepository;


    @Override
    public long count() {
        return ProductRepository.count();
    }

    @Override
    public Product save(Product product) {
        return ProductRepository.save(product);
    }

    @Override
    public void delete(Product product) {
        ProductRepository.delete(product);
    }

    @Override
    public Iterable<Product> getAll() {
        return ProductRepository.findAll();
    }

    @Override
    public List<Product> getByName(String name) {
        List<Product> list = new ArrayList<>();
        MatchQueryBuilder matchQueryBuilder = new MatchQueryBuilder("name", name);
        Iterable<Product> iterable = ProductRepository.search(matchQueryBuilder);
        iterable.forEach(e->list.add(e));
        return list;
    }

    @Override
    public Page<Product> pageQuery(Integer pageNo, Integer pageSize, String kw) {
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.matchPhraseQuery("name", kw))
                .withPageable(PageRequest.of(pageNo, pageSize))
                .build();
        return ProductRepository.search(searchQuery);
    }


}

(左右滑动查看全部代码)

以上我们展示了对es中数据的增删改查和分页查询操作。

最后,我们写一个测试类来测试其中的方法


package com.primeton.example;

import com.primeton.example.entity.Product;
import com.primeton.example.service.ProductService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;

@RunWith(SpringRunner.class)
@SpringBootTest
public class ElasticsearchRepositoriesTests {

    @Autowired
    private ProductService productService;

    @Test
    public void testCount() {
        System.out.println(ProductService.count());
    }

    @Test
    public void testInsert() {
        Product product = new Product();
        product.setProductId("0901009001");
        product.setName("奶油夹心面包(10片装)");
        product.setCategory("101");
        product.setPrice(880);
        product.setBrand("徐福记");
        productService.save(product);

        product = new Product();
        product.setProductId("0901009002");
        product.setName("切片面包(10片装)");
        product.setCategory("101");
        product.setPrice(680);
        product.setBrand("巴莉甜甜");
        productService.save(product);

        product = new Product();
        product.setProductId("0901009004");
        product.setName("原味吐司850g");
        product.setCategory("101");
        product.setPrice(720);
        product.setBrand("采蝶轩");
        productService.save(product);

    }

    @Test
    public void testDelete() {
        Product product = new Product();
        product.setProductId("0901009002");
        productService.delete(product);
    }

    @Test
    public void testGetAll() {
        Iterable<Product> iterable = productService.getAll();
        iterable.forEach(e->System.out.println(e.toString()));
    }

    @Test
    public void testGetByName() {
        List<Product> list = productService.getByName("面包");
        System.out.println(list);
    }

    @Test
    public void testPage() {
        Page<Product> page = productService.pageQuery(0, 10, "切片");
        System.out.println(page.getTotalPages());
        System.out.println(page.getNumber());
        System.out.println(page.getContent());
    }
}

(左右滑动查看全部代码)

以上,就是使用Spring Data Elasticsearch Repositories 操作Elasticsearch的demo示例。

2. 使用ElasticsearchTemplate操作Elasticsearch


package com.primeton.example;

import com.primeton.example.entity.Product;
import org.elasticsearch.index.query.QueryBuilders;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.query.*;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;

@RunWith(SpringRunner.class)
@SpringBootTest
public class ElasticsearchTemplateTest {

    @Autowired
    public ElasticsearchTemplate elasticsearchTemplate;

    @Test
    public void testInsert() {
        Product product = new Product();
        product.setProductId("0901009005");
        product.setName("葡萄吐司面包(10片装)");
        product.setCategory("101");
        product.setPrice(560);
        product.setBrand("采蝶轩");

        IndexQuery indexQuery = new IndexQueryBuilder().withObject(product).build();
        elasticsearchTemplate.index(indexQuery);
    }

    @Test
    public void testQuery() {
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.matchQuery("name", "吐司"))
                .build();
        List<Product> list = elasticsearchTemplate.queryForList(searchQuery, Product.class);
        System.out.println(list);
    }

}

(左右滑动查看全部代码)

引入的ElasticsearchTemplate是自动装配的:

以上,就是使用ElasticsearchTemplate 操作Elasticsearch的demo示例。

6.总结

由于篇幅有限,本篇文章给大家介绍了Elasticsearch的基本概念,Elasticsearch具有哪些功能、特点以及使用场景,Elasticsearch的倒排索引结构、并且我们从实战的角度将Elasticsearch和Springboot整合到了一起,使其可以成为一个基础的搜索服务,给系统中其它的微服务应用提供服务。

Elasticsearch不仅仅是一个搜索引擎,同时它还是一个优秀的分布式系统,其分布式架构非常值得我们学习,下一篇文章,将重点讨论Elasticsearch的分布式架构,希望能从其架构中学到一些架构经验,为我们自己构建一个分布式系统提供一些方向。

关于作者:西西西西索,现任普元微服务团队java开发工程师,长期致力于IT技术研究,微服务架构设计和开发等工作。擅长springboot、springcloud、elasticsearch、kafka等技术。先后参加EOS MS5.0GA、EOS PLATFORM 8.1LA、上海公安项目的研发工作。