脚本之家

电脑版
提示:原网页已由神马搜索转码, 内容由www.jb51.net提供.
您的位置:首页软件编程java→ Spring Boot ElasticSearch

Spring Boot集成ElasticSearch实现搜索引擎的示例

  更新时间:2017年11月02日 10:04:35  作者:牛麦康纳 
这篇文章主要介绍了Spring Boot集成ElasticSearch实现搜索引擎的示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

Elastic Search是一个开源的,分布式,实时搜索和分析引擎。Spring Boot为Elasticsearch及Spring Data Elasticsearch提供的基于它的抽象提供了基本的配置。Spring Boot提供了一个用于聚集依赖的spring-boot-starter-data-elasticsearch 'StarterPOM'。

ElasticSearch作为搜索引擎,我们需要解决2大问题:

1,  如何将被搜索的数据在ES上创建反向索引
2,  Java代码如何与ES交互

其中第一个大问题又分为两个小问题

1.1,如何初始化已有的数据
1.2,如何同步增量数据

第二个大问题也有两种集成方式

2.1 Spring Data 9300端口集成
2.2 Restful API 9200端口集成

本篇先解决第二大问题。

第一种方式,利用RestAPI方式,也叫Jest方式:

示例代码:https://github.com/yejingtao/forblog/tree/master/demo-jest-elasticsearch

Pom.xml:

<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>yejingtao.demo.springcloud</groupId>
<artifactId>demo-jest-elasticsearch</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>demo-jest-elasticsearch</name>
<url>http://maven.apache.org</url>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.6.RELEASE</version>
</parent>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>io.searchbox</groupId>
<artifactId>jest</artifactId>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
</dependency>
</dependencies>
</project>

Application.yml:

server: 
port: 7081

spring:
elasticsearch:
jest:
uris:
- http://192.168.226.133:9200
read-timeout: 5000

注意这里是9200端口

主程序:最简单的Spring boot启动程序:

@SpringBootApplication 
public class ESApplication {

public static void main(String[] args) {
SpringApplication.run(ESApplication.class);
}
}

定义好ES中的实体类和对ES操作的接口:

public class Entity implements Serializable{ 

private static final long serialVersionUID = -763638353551774166L;

public static final String INDEX_NAME = "index_entity";

public static final String TYPE = "tstype";

private Long id;

private String name;

public Entity() {
super();
}

public Entity(Long id, String name) {
this.id = id;
this.name = name;
}

public Long getId() {
return id;
}

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

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}


}
public interface CityESService { 

void saveEntity(Entity entity);

void saveEntity(List<Entity> entityList);

List<Entity> searchEntity(String searchContent);
}

接口实现:

@Service 
public class CityESServiceImpl implements CityESService{

private static final Logger LOGGER = LoggerFactory.getLogger(CityESServiceImpl.class);

@Autowired
private JestClient jestClient;

@Override
public void saveEntity(Entity entity) {
Index index = new Index.Builder(entity).index(Entity.INDEX_NAME).type(Entity.TYPE).build();
try {
jestClient.execute(index);
LOGGER.info("ES 插入完成");
} catch (IOException e) {
e.printStackTrace();
LOGGER.error(e.getMessage());
}
}


/**
* 批量保存内容到ES
*/
@Override
public void saveEntity(List<Entity> entityList) {
Bulk.Builder bulk = new Bulk.Builder();
for(Entity entity : entityList) {
Index index = new Index.Builder(entity).index(Entity.INDEX_NAME).type(Entity.TYPE).build();
bulk.addAction(index);
}
try {
jestClient.execute(bulk.build());
LOGGER.info("ES 插入完成");
} catch (IOException e) {
e.printStackTrace();
LOGGER.error(e.getMessage());
}
}

/**
* 在ES中搜索内容
*/
@Override
public List<Entity> searchEntity(String searchContent){
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//searchSourceBuilder.query(QueryBuilders.queryStringQuery(searchContent));
//searchSourceBuilder.field("name");
searchSourceBuilder.query(QueryBuilders.matchQuery("name",searchContent));
Search search = new Search.Builder(searchSourceBuilder.toString())
.addIndex(Entity.INDEX_NAME).addType(Entity.TYPE).build();
try {
JestResult result = jestClient.execute(search);
return result.getSourceAsObjectList(Entity.class);
} catch (IOException e) {
LOGGER.error(e.getMessage());
e.printStackTrace();
}
return null;
}
}

这里插入数据的方式给了两种,一种是单次API直接插入,一种是利用ES的bulk批量插入。

做一个controller方面我们测试:

启动后在浏览器中请求http://localhost:7081/entityController/search?name=%E4%BA%BA%E6%89%8B%E4%BA%95

得到结果:

这里只返回了9条记录,而理论上ES默认的size是10,应该不是分页的问题,而是只能检索出9条匹配记录,用Kibana连上相同的搜索确认下:

这里用的是standard分词方式,将每个中文都作为了一个term,凡是包含“人”“手”“井”的都被搜索了出来,只是评分不同,如果想支持只能中文索引需要依赖ik插件

OK,RestFul方式对ElasticSearch的检索已经搞定了,更多的扩展可以慢慢研究下QueryBuilders里的源码和批注。

第二种方式,利用Spring Data客户端方式:

事先说明此方式有个弊端,让我掉了坑里好久才爬上来,Spring Data ElasticSearch必须与ElasticSearch版本相匹配,否则在对接时ES端会报版本不匹配错误,例如我ES是5.6.1版本,Spring boot是1.5.6版本,错误如下:

为解决这个问题我查找了一些资料,Spring Data与elasticsearch版本对应关系如下:

spring data elasticsearch

elasticsearch

3.0.0.RC2

5.5.0

3.0.0.M4

5.4.0

2.0.4.RELEASE

2.4.0

2.0.0.RELEASE

2.2.0

1.4.0.M1

1.7.3

1.3.0.RELEASE

1.5.2

1.2.0.RELEASE

1.4.4

1.1.0.RELEASE

1.3.2

1.0.0.RELEASE

1.1.1

而我用的Spring Boot 1.5.6版本对应的Spring Data ElasticSearch是2.1.6版本,不支持5.X的ES,所以报错。到本博文撰写为止,Spring Boot的RELEASE版本最新的是1.5.8,对应的Spring Data ElasticSearch是2.1.8,仍不支持5.X的ES,所以如果一定要使用Java客户端方式集成ES只能放弃Spring Boot直接使用Spring Data和Spring MVC,或者降低ES的版本使之与Spring boot匹配。

示例代码:https://github.com/yejingtao/forblog/tree/master/demo-data-elasticsearch

pom.xml依赖:

<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>yejingtao.demo.springcloud</groupId>
<artifactId>demo-data-elasticsearch</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>demo-data-elasticsearch</name>
<url>http://maven.apache.org</url>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.8.RELEASE</version>
</parent>

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

不再引用Jest。

application.yml:

server: 
port: 7081

spring:
data:
elasticsearch:
cluster-nodes: 192.168.226.133:9300
cluster-name: my-es
repositories:
enabled: true

注意这里是9300端口

Controller、主程序、Service接口同Jest项目不变,不再罗列

实体类稍作变化,指定ES中的index和type:

@Document(indexName="index_entity", type="tstype") 

多一个Repository接口,无需实现类,spring data标准用法:

/** 
* Entity ES操作类
* @author yejingtao
*
*/
public interface EntityRepository extends ElasticsearchRepository<Entity,Long>{

}

Service实现类与Jest的天壤之别了,从语法上可以看出更像是对数据库层的操作:

@Service 
public class CityESServiceImpl implements CityESService{

private static final Logger LOGGER = LoggerFactory.getLogger(CityESServiceImpl.class);

int PAGE_SIZE = 15; //默认分页大小

int PAGE_NUMBER = 0; //默认当前分页

String SCORE_MODE_SUM = "sum"; //权重分求和模式

Float MIN_SCORE = 10.0F; //由于无相关性的分值默认为1, 设置权重分最小值为10

@Autowired
EntityRepository entityRepository;

/**
* 保存内容到ES
*/
@Override
public Long saveEntity(Entity entity) {
Entity entityResult = entityRepository.save(entity);
return entityResult.getId();
}

/**
* 在ES中搜索内容
*/
@Override
public List<Entity> searchEntity(int pageNumber, int pageSize, String searchContent){
if(pageSize==0) {
pageSize = PAGE_SIZE;
}
if(pageNumber<0) {
pageNumber = PAGE_NUMBER;
}

SearchQuery searchQuery = getEntitySearchQuery(pageNumber,pageSize,searchContent);

LOGGER.info("\n searchCity: searchContent [" + searchContent + "] \n DSL = \n "
+ searchQuery.getQuery().toString());


Page<Entity> cityPage = entityRepository.search(searchQuery);
return cityPage.getContent();
}

/**
* 组装搜索Query对象
* @param pageNumber
* @param pageSize
* @param searchContent
* @return
*/
private SearchQuery getEntitySearchQuery(int pageNumber, int pageSize, String searchContent) {
FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery()
.add(QueryBuilders.matchPhraseQuery("name", searchContent),
ScoreFunctionBuilders.weightFactorFunction(1000))
//.add(QueryBuilders.matchPhraseQuery("other", searchContent),
//ScoreFunctionBuilders.weightFactorFunction(1000))
.scoreMode(SCORE_MODE_SUM).setMinScore(MIN_SCORE);
//设置分页,否则只能按照ES默认的分页给
Pageable pageable = new PageRequest(pageNumber, pageSize);
return new NativeSearchQueryBuilder().withPageable(pageable).withQuery(functionScoreQueryBuilder).build();
}

}

测试方式同Jest。

这两种方式,从设计上来讲属于两种思路,Spring Data的思路就是将ElasticSearch当自家的数据仓库来管理,直接通过Java客户端代码操作ES;Jest的思路是将ElasticSearch当为独立的服务端,自己作为客户端用兼容性最强的RestFul格式来与之交互。
个人比较倾向于Jest方式,第一兼容性好,不需要考虑版本的问题。第二,从ElasticSearch本身的设计上来分析,9200是对外服务端口,9300是内部管理和集群通信端口,请求9200获取搜索服务更符合ES的设计初衷,不会影响集群内部的通信。
以上比较分析仅代表个人观点,欢迎大神么交流批评。希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

    • 这篇文章主要给大家介绍了关于spring boot高并发下耗时操作的实现方法,文中通过示例代码介绍的非常详细,对大家学习或者使用spring boot具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
      2019-11-11
    • 在Java中,字符串是一种常见的数据类型,我们经常需要对其进行加密和解密,本文主要介绍了Java长字符串加密的实现,具有一定的参考价值,感兴趣的可以了解一下
      2024-02-02
    • 在本篇文章里小编给大家总结了关于Java的call by value和call by reference的相关用法和知识点内容,需要的朋友们学习下。
      2019-03-03
    • 今天带大家学习Spring的特性-工厂特性,文中有非常详细的介绍及代码示例,对正在学习java的小伙伴们有很好地帮助,需要的朋友可以参考下
      2021-05-05
    • 这篇文章介绍了Java中去除字符串中所有空格的几种方法,有需要的朋友可以参考一下
      2013-07-07
    • 我们可以通过fastdfs实现一个分布式文件系统,如果fastdfs部署在外网,那么任何一个人知道了上传接口,就可以实现文件的上传和访问。那么如何阻止他人访问我们fastdfs服务器上的文件呢?此处就需要使用fastdfs的防盗链功能,本文就来讲讲如何实现这一功能
      2022-10-10
    • 这篇文章主要为大家详细介绍了java多态实现电子宠物系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
      2022-02-02
    • 这篇文章主要介绍了Java8函数式接口UnaryOperator用法示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
      2023-07-07
    • 在平时的业务模块开发过程中,难免会需要做一些全局的任务、缓存、线程等等的初始化工作,那么如何解决这个问题呢?本文就来介绍一下,感兴趣的可以了解一下
      2021-07-07
    • 本文主要介绍了springboot接入mq的方法示例,主要实现配置以及实现一个简单的发送、接收消息的例子,具有一定的参考价值,感兴趣的可以了解一下
      2023-09-09

    最新评论