SpringBoot集成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⽅式:
<project xmlns="/POM/4.0.0" xmlns:xsi="/2001/XMLSchema-instance"
xsi:schemaLocation="/POM/4.0.0 /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></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>
server:
port: 7081
spring:
elasticsearch:
jest:
uris:
- 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 = Logger(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 {
LOGGER.info("ES 插⼊完成");
} catch (IOException e) {
e.printStackTrace();
<(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 {
LOGGER.info("ES 插⼊完成");
} catch (IOException e) {
e.printStackTrace();
<(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.String())
.addIndex(Entity.INDEX_NAME).addType(Entity.TYPE).build();
try {
JestResult result = ute(search);
SourceAsObjectList(Entity.class);
} catch (IOException e) {
<(e.getMessage());
e.printStackTrace();
}
return null;
}
}
这⾥插⼊数据的⽅式给了两种,⼀种是单次API直接插⼊,⼀种是利⽤ES的bulk批量插⼊。
做⼀个controller⽅⾯我们测试:
得到结果:
这⾥只返回了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匹配。
<project xmlns="/POM/4.0.0" xmlns:xsi="/2001/XMLSchema-instance"
xsi:schemaLocation="/POM/4.0.0 /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></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。
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 = Logger(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);
Id();
}
spring boot选择题/**
* 在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 "
+ Query().toString());
Page<Entity> cityPage = entityRepository.search(searchQuery);
Content();
}
/**
* 组装搜索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的设计初衷,不会影响集内部的通信。
以上⽐较分析仅代表个⼈观点,欢迎⼤神么交流批评。希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论