多平台统一管理软件接口,如何实现多平台统一管理软件接口
351
2023-01-23
SpringBoot 整合Jest实例代码讲解
【1】添加Elasticsearch-starter
pom文件添加starter如下:
SpringBoot默认支持两种技术和Elasticsearch进行交互:Spring Data Elasticsearch和Jest。
Jest默认不生效,需要导入io.searchbox.client.JestClient。
maven依赖如下:
Spring Data Elasticsearch主要作用如下:
① ElasticsearchAutoConfiguration中注册了client,属性有clusterNodes和clusterName。
② ElasticsearchDataAutoConfiguration注册了ElasticsearchTemplate来操作ES
@Configuration
@ConditionalOnClass({ Client.class, ElasticsearchTemplate.class })
@AutoConfigureAfter(ElasticsearchAutoConfiguration.class)
public class ElasticsearchDataAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(Client.class)
public ElasticsearchTemplate elasticsearchTemplate(Client client,
ElasticsearchConverter converter) {
try {
return new ElasticsearchTemplate(client, converter);
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
}
@Bean
@ConditionalOnMissingBean
public ElasticsearchConverter elasticsearchConverter(
SimpleElasticsearchMappingContext mappingContext) {
return new MappingElasticsearchConverter(mappingContext);
}
@Bean
@ConditionalOnMissingBean
public SimpleElasticsearchMappingContext mappingContext() {
return new SimpleElasticsearchMappingContext();
}
}
③ ElasticsearchRepositoriesAutoConfiguration 启用了ElasticsearchRepository
@Configuration
@ConditionalOnClass({ Client.class, ElasticsearchRepository.class })
@ConditionalOnProperty(prefix = "spring.data.elasticsearch.repositories", name = "enabled", havingValue = "true", matchIfMissing = true)
@ConditionalOnMissingBean(ElasticsearchRepositoryFactoryBean.class)
@Import(ElasticsearchRepositoriesRegistrar.class)
public class ElasticsearchRepositoriesAutoConfiguration {
KLlNaNvHvq}
ElasticsearchRepository接口源码如下(类似于JPA中的接口):
@NoRepositoryBean
public interface ElasticsearchRepository
S index(S var1);
Iterable
Page
Page
Page
void refresh();
Class
}
【2】JestClient操作测试
application.properties配置如下:
# jest url配置
spring.elasticsearch.jest.uris=http://192.168.2.110:9200
测试类如下:
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBootJestTest {
@Autowired
JestClient jestClient;
@Test
public void index(){
Article article = new Article();
article.setId(1);
article.setAuthor("Tom");
article.setContent("hello world !");
article.setTitle("今日消息");
//构建一个索引功能,类型为news
Index index = new Index.Builder(article).index("jest").type("news").build();
try {
jestClient.execute(index);
System.out.println("数据索引成功!");
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void search(){
//查询表达式
String json = "{\n" +
" \"query\" : {\n" +
" \"match\" : {\n" +
" \"content\" : \"hello\"\n" +
" }\n" +
" }\n" +
"}";
//构建搜索功能
Search search = new Search.Builder(json).addIndex("jest").addType("news").build();
try {
SearchResult result = jestClient.execute(search);
System.out.println(result.getJsonString());
} catch (IOException e) {
e.printStackTrace();
}
}
}
测试存储数据结果如下:
测试查询数据结果如下:
【3】 Elasticsearch版本调整
application.properties进行配置:
# Spring data elasticsearch配置
spring.data.elasticsearch.cluster-name=elasticsearch
spring.data.elasticsearch.cluster-nodes=192.168.2.110:9300
这里节点名取自如下图:
启动主程序,可能报错如下(ES版本不合适):
查看Spring Data官网,其中spring data elasticsearch与elasticsearch适配表如下:
官网地址:https://github.com/spring-projects/spring-data-elasticsearch
我们在上篇博文中安装的ES版本为5.6.10,项目中SpringBoot版本为1.5.12,spring-boot-starter-data-elasticsearch为2.1.11,elasticsearch版本为2.4.6。
两种解决办法:① 升级SpringBoot版本;② 安装2.4.6版本的elasticsearch。
这里修改暴露的端口,重新使用docker安装2.4.6版本:
# 拉取2.4.6 镜像
docker pull registry.docker-cn.com/library/elasticsearch:2.4.6
# 启动容器
docker run -e ES_java_OPTS="-Xms256m -Xmx256m" -d -p 9201:9200 -p 9301:9300 --name ES02 bc337c8e4f
application.properties配置文件同步修改:
# jest url配置
spring.elasticsearch.jest.uris=http://192.168.2.110:9201
# Spring data elasticsearch配置
spring.data.elasticsearch.cluster-name=elasticsearch
spring.data.elasticsearch.cluster-nodes=192.168.2.110:9301
此时再次启动程序:
【4】ElasticsearchRepository使用
类似于JPA,编写自定义Repository接口,继承自ElasticsearchRepository:
public interface BookRepository extends ElasticsearchRepository
public List
}
这里第一个参数为对象类型,第二个参数为对象的主键类型。
BookRepository 所拥有的方法如下图:
Book源码如下:
// 这里注意注解
@Document(indexName = "elastic",type = "book")
public class Book {
private Integer id;
private String bookName;
private String author;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
@Override
public String toString() {
return "Book{" +
"id=" + id +
", bookName='" + bookName + '\'' +
", author='" + author + '\'' +
'}';
}
}
测试类如下:
@Autowired
BookRepository bookRepository;
@Test
public void testRepository(){
Book book = new Book();
book.setAuthor("吴承恩");
book.setBookName("西游记");
book.setId(1);
bookRepository.index(book);
System.out.println("BookRepository 存入数据成功!");
}
测试结果如下图:
测试获取示例如下:
@Test
public void testRepository2(){
for (Book book : bookRepository.findByBookNameLike("游")) {
System.out.println("获取的book : "+book);
} ;
Book book = bookRepository.findOne(1);
System.out.println("根据id查询 : "+book);
}
测试结果如下图:
Elasticsearch支持方法关键字如下图所示
即,在BookRepository中使用上述关键字构造方法,即可使用,Elastic自行实现其功能!
支持@Query注解
如下所示,直接在方法上使用注解:
public interface BookRepository extends ElasticsearchRepository
@Query("{"bool" : {"must" : {"field" : {"name" : "?0"}}}}")
Page
}
【5】ElasticsearchTemplate使用
存入数据源码示例如下:
@Autowired
ElasticsearchTemplate elasticsearchTemplate;
@Test
public void testTemplate01(){
Book book = new Book();
book.setAuthor("曹雪芹");
book.setBookName("红楼梦");
book.setId(2);
IndexQuery indexQuery = new IndexQueryBuilder().withId(String.valueOf(book.getId())).withObject(book).build();
elasticsearchTemplate.index(indexQuery);
}
测试结果如下:
查询数据示例如下:
@Test
public void testTemplate02(){
QueryStringQueryBuilder stringQueryBuilder = new QueryStringQueryBuilder("楼");
stringQueryBuilder.field("bookName");
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(stringQueryBuilder).build();
Page
Iterator
while(iterator.hasNext()){
Book book = iterator.next();
System.out.println("该次获取的book:"+book);
}
}
测试结果如下:
开源项目: https://github.com/spring-projects/spring-data-elasticsearch
https://github.com/searchbox-io/Jest/tree/master/jest
1.说明
本文主要讲解如何使用Spring Boot快速搭建Web框架,结合Spring Data 和 Jest 快速实现对阿里云ElasticSearch的全文检索功能。
主要使用组件:
Spring Boot Starter:可以帮助我们快速的搭建spring mvc 环境
Jest:一种rest访问es的客户端
elasticsearch:全文检索
spring data elasticsearch:结合spring data
thymeleaf:web前端模版框架
jquery:js框架
bootstrap:前端样式框架
2.项目Maven配置
以下为项目Maven配置,尤其需要注意各个组件的版本,以及注释部分。
各个组件的某些版本组合下回出现各种异常,以下maven为测试可通过的一个版本。
xmlns:xsi="http://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">
xmlns:xsi="http://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">
创建完成后,项目目录结构如下:
3.Spring Starter配置需使用SpringBootApplication启动需禁用ElasticsearchAutoConfiguration,ElasticsearchDataAutoConfiguration,否则会有异常HighLightJestSearchResultMapper Bean留待下面解释,主要为了解决spring data不支持elasticsearch检索highlight问题,此处为该Bean的注册
@SpringBootApplication
@EnableAutoConfiguration(exclude = {ElasticsearchAutoConfiguration.class, ElasticsearchDataAutoConfiguration.class})
public class App {
public static void main(String[] args) throws Exception {
SpringApplication.run(App.class, args);
}
@Bean
public HighLightJestSearchResultMapper highLightJestSearchResultMapper(){
return new HighLightJestSearchResultMapper();
}
}
3.Entity配置
a) 歌曲Entity如下:
通过对Class进行Document注解,实现与ElasticSearch中的Index和Type一一对应。
该类在最终与ES返回结果映射时,仅映射其中_source部分。即如下图部分(highlight另说,后面单独处理了):
@Document(indexName = "songs",type = "sample",shards = 1, replicas = 0, refreshInterval = "-1")
public class Song extends HighLightEntity{
@Id
private Long id;
private String name;
private String href;
private String lyric;
private String singer;
private String album;
public Song(Long id, String name, String href, String lyric, String singer, String album, Map
//省略
}
public Song() {
}
//getter setter 省略...
}
b) 为了解决Spring data elasticsearch问题,此处增加一个抽象类:HighLightEntity,其他Entity需要继承该类
package org.leiws.esweb.entity;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
public abstract class HighLightEntity implements Serializable{
private Map
public Map
return highlight;
}
public void setHighlight(Map
this.highlight = highlight;
}
}
4.Repository配置
package org.leiws.esweb.repository;
import org.leiws.esweb.entity.Song;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
public interface SongRepository extends ElasticsearchRepository
}
5.Service配置
a) 接口
package org.leiws.esweb.service;
import org.leiws.esweb.entity.Song;
import org.springframework.data.domain.Page;
import java.util.List;
/**
* The interface Song service.
*/
public interface SongService {
/**
* Search song list.
*
* @param pNum the p num
* @param pSize the p size
* @param keywords the keywords
* @return the list
*/
public Page
}
b) 实现类
该类实现了具体如何分页,如何查询等
package org.leiws.esweb.service.impl;
import com.github.vanroy.springdata.jest.JestElasticsearchTemplate;
import org.apache.log4j.Logger;
import org.elasticsearch.common.lucene.search.function.FiltersFunctionScoreQuery;
import org.elasticsearch.index.query.MatchPhraseQueryBuilder;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.leiws.esweb.entity.Song;
import org.leiws.esweb.repository.HighLightJestSearchResultMapper;
import org.leiws.esweb.repository.SongRepository;
import org.leiws.esweb.service.SongService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.stereotype.Service;
import static org.elasticsearch.index.query.QueryBuilders.functionScoreQuery;
import static org.elasticsearch.index.query.QueryBuilders.matchPhraseQuery;
import java.util.List;
@Service
public class SongServiceImpl implements SongService{
private static final Logger LOGGER = Logger.getLogger(SongServiceImpl.class);
/* 分页参数 */
private final static Integer PAGE_SIZE = 12; // 每页数量
private final static Integer DEFAULT_PAGE_NUMBER = 0; // 默认当前页码
/* 搜索模式 */
private final static String SCORE_MODE_SUM = "sum"; // 权重分求和模式
private final static Float MIN_SCORE = 10.0F; // 由于无相关性的分值默认为 1 ,设置权重分最小值为 10
@Autowired
SongRepository songRepository;
@Autowired
JestElasticsearchTemplate jestElasticsearchTemplate;
@Autowired
HighLightJestSearchResultMapper jestSearchResultMapper;
@Override
public Page
// 校验分页参数
if (pSize == null || pSize <= 0) {
pSize = PAGE_SIZE;
}
if (pNum == null || pNum < DEFAULT_PAGE_NUMBER) {
pNum = DEFAULT_PAGE_NUMBER;
}
LOGGER.info("\n searchCity: searchContent [" + keywords + "] \n ");
// 构建搜索查询
SearchQuery searchQuery = getCitySearchQuery(pNum,pSize,keywords);
LOGGER.info("\n searchCity: searchContent [" + keywords + "] \n DSL = \n " + searchQuery.getQuery().toString());
// Page
Page
return cityPage;
}
/**
* 根据搜索词构造搜索查询语句
*
* 代码流程:
* - 权重分查询
* - 短语匹配
* - 设置权重分最小值
* - 设置分页参数
*
* @param pNum 当前页码
* @param pSize 每页大小
* @param searchContent 搜索内容
* @return
*/
private SearchQuery getCitySearchQuery(Integer pNum, Integer pSize,String searchContent) {
/* elasticsearch 2.4.6 版本写法
FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery()
.add(QueryBuilders.boolQuery().should(QueryBuilders.matchQuery("lyric", searchContent)),
ScoreFunctionBuilders.weightFactorFunction(1000))
.scoreMode(SCORE_MODE_SUM).setMinScore(MIN_SCORE);
*/
FunctionScoreQueryBuilder.FilterFunctionBuilder[] functions = {
new FunctionScoreQueryBuilder.FilterFunctionBuilder(
matchPhraseQuery("lyric", searchContent),
ScoreFunctionBuilders.weightFactorFunction(1000))
};
FunctionScoreQueryBuilder functionScoreQueryBuilder =
functionScoreQuery(functions).scoreMode(FiltersFunctionScoreQuery.ScoreMode.SUM).setMinScore(MIN_SCORE);
// 分页参数
// Pageable pageable = new PageRequest(pNum, pSize);
Pageable pageable = PageRequest.of(pNum, pSize);
//高亮提示
HighlightBuilder.Field highlightField = new HighlightBuilder.Field("lyric")
.preTags(new String[]{"", "", ""})
.postTags(new String[]{"", "", ""})
.fragmentSize(15)
.numOfFragments(5)
//highlightQuery必须单独设置,否则在使用FunctionScoreQuery时,highlight配置不生效,返回结果无highlight元素
//官方解释:Highlight matches for a query other than the search query. This is especially useful if you use a rescore query because those are not taken into account by highlighting by default.
.highlightQuery(matchPhraseQuery("lyric", searchContent));
return new NativeSearchQueryBuilder()
.withPageable(pageable)
// .withSourceFilter(new FetchSourceFilter(new String[]{"name","singer","lyric"},new String[]{}))
.withHighlightFields(highlightField)
.withQuery(functionScoreQueryBuilder).build();
}
}
c) 解决Spring Data ElasticSearch不支持Highlight的问题
通过自定义实现一个如下的JestSearchResultMapper,解决无法Highlight的问题
package org.leiws.esweb.repository;
//import 省略
public class HighLightJestSearchResultMapper extends DefaultJestResultsMapper {
private EntityMapper entityMapper;
private MappingContext extends ElasticsearchPersistentEntity>, ElasticsearchPersistentProperty> mappingContext;
public HighLightJestSearchResultMapper() {
this.entityMapper = new DefaultEntityMapper();
this.mappingContext = new SimpleElasticsearchMappingContext();
}
public HighLightJestSearchResultMapper(MappingContext extends ElasticsearchPersistentEntity>, ElasticsearchPersistentProperty> mappingContext, EntityMapper entityMapper) {
this.entityMapper = entityMapper;
this.mappingContext = mappingContext;
}
public EntityMapper getEntityMapper() {
return entityMapper;
}
public void setEntityMapper(EntityMapper entityMapper) {
this.entityMapper = entityMapper;
}
@Override
public
return mapResults(response, clazz, null);
}
@Override
public
LinkedList
for (SearchResult.Hit
if (hit != null) {
T result = mapSource(hit.source, clazz);
HighLightEntity highLightEntity = (HighLightEntity) result;
highLightEntity.setHighlight(hit.highlight);
results.add((T) highLightEntity);
}
}
String scrollId = null;
if (response instanceof ExtendedSearchResult) {
scrollId = ((ExtendedSearchResult) response).getScrollId();
}
return new AggregatedPageImpl<>(results, response.getTotal(), response.getAggregations(), scrollId);
}
private
String sourceString = source.toString();
T result = null;
if (!StringUtils.isEmpty(sourceString)) {
result = mapEntity(sourceString, clazz);
setPersistentEntityId(result, source.get(JestResult.ES_METADATA_ID).getAsString(), clazz);
} else {
//TODO(Fields results) : Map Fields results
//result = mapEntity(hit.getFields().values(), clazz);
}
return result;
}
private
if (isBlank(source)) {
return null;
}
try {
return entityMapper.mapToObject(source, clazz);
} catch (IOException e) {
throw new ElasticsearchException("failed to map source [ " + source + "] to class " + clazz.getSimpleName(), e);
}
}
private
ElasticsearchPersistentEntity> persistentEntity = mappingContext.getRequiredPersistentEntity(clazz);
ElasticsearchPersistentProperty idProperty = persistentEntity.getIdProperty();
// Only deal with text because ES generated Ids are strings !
if (idProperty != null) {
if (idProperty.getType().isAssignableFrom(String.class)) {
persistentEntity.getPropertyAccessor(entity).setProperty(idProperty, id);
}
}
}
}
上面类的大部分代码来源于:DefaultJestResultsMapper
重点修改部分为:
@Override
public
LinkedList
for (SearchResult.Hit
if (hit != null) {
T result = mapSource(hit.source, clazz);
HighLightEntity highLightEntity = (HighLightEntity) result;
highLightEntity.setHighlight(hit.highlight);
results.add((T) highLightEntity);
}
}
String scrollId = null;
if (response instanceof ExtendedSearchResult) {
scrollId = ((ExtendedSearchResult) response).getScrollId();
}
return new AggregatedPageImpl<>(results, response.getTotal(), response.getAggregations(), scrollId);
}
6.Controller
相对简单,如普通的Spring Controller
@Controller
@RequestMapping(value = "/search")
public class SearchController {
@Autowired
SongService songService;
/**
* Song list string.
*
* @param map the map
* @return the string
*/
@RequestMapping(method = RequestMethod.GET)
public String songList(@RequestParam(value = "pNum") Integer pNum,
@RequestParam(value = "pSize", required = false) Integer pSize,
@RequestParam(value = "keywords") String keywords,ModelMap map){
map.addAttribute("pageSong",songService.searchSong(pNum,pSize,keywords));
return "songList";
}
}
7.前端页面thymeleaf模版
存放目录为:resources/templates/songList.html
为您找到0个结果:
...
Previous
Next
8.阿里云ElasticSearch连接配置
在resources/application.properties中配置如下:
spring.data.jest.uri=http://1xx.xxx.xxx.xxx:8080
spring.data.jest.username=username
spring.data.jest.password=password
spring.data.jest.maxTotalConnection=50
spring.data.jest.defaultMaxTotalConnectionPerRoute=50
spring.data.jest.readTimeout=5000
9.其他
a) thymeleaf 热启动配置,便于测试在resources/application.properties中配置如下:
spring.thymeleaf.cache=false
在pom.xml中增加:
3.每次还是需要重新compile后,修改的thymeleaf模版代码才会自动生效,因为spring boot启动是以target目录为准的
b) 阿里云elasticsearch在esc上配置ngnix代理,以支持本机可以公网访问,便于开发购买一台esc在ecs上安装ngnix,并配置代理信息server 部分如下:
server {
listen 8080;
#listen [::]:80 default_server;
server_name {本机内网ip} {本机外网ip};
#root /usr/share/nginx/html;
# Load configuration files for the default server block.
#include /etc/nginx/default.d/*.conf;
location / {
proxy_pass http://{elasticsearch 内网 ip}:9200;
}
}
10. 最后,查询效果:
总结
以上所述是给大家介绍的SpringBoot 整合Jest实例代码,希望对大家有所帮助,如果大家有任何疑问请给我留言,会及时回复大家的。在此也非常感谢大家对我们网站的支持!
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~