springboot结合elasticsearch高亮显示查询 spring-data elasticsearch java api(含有源码下载)
springboot的整合spring-data该怎么整合网上这些东西太多了,大家可以百度一下。而且在文章最下面已经提供了该博客的所有源码。希望大家能够follow and star me。
我本来按照下面的方法进行查找,返回的内容就是FilmEntity实体bean映射的内容。如果我要是高亮查询内容,按照@query注解中添加highlight查询,name压根映射不上,真是痛苦。网上搜了一大堆千篇一律,都是我下面的那个使用spring-data的方法。不能说spring-data的crud不好,但是在向es导入数据的时候,spring-data的crud还是非常好用的。
然而在一些复杂的查询时候,spring-data封装的crud就不好用。我写了一个方法专门针对复杂类型的查询,我测试了高亮和多字段查询,封装返回实体bean的集合。
这样拿着spring-data的crud,esclient混合使用可以更好的达到特定的业务目的。
public interface FilmDao extends ElasticsearchRepository<FilmEntity, Long>{
@Query("{\"bool\": {\"must\": [{\"match\": {\"name\": \"?0\"}}]}},\"highlight\": {\n" +
"\"fields\" : {\n" +
"\"name\" : {}\n" +
"}\n" +
"}")
List<Object> findByAuthorsNameUsingCustomQuery(String name);
看了很多文档,总结写了一个config以注解的形式,就像mongotemplate那样使用es,效果很好。
大概意思就是在项目启动后生成一个client,然后大家都使用这一个client对elasticsearch进行操作。
import org.elasticsearch.client.Client;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.net.InetAddress;
/**
* <p>description: 创建TransportClient的类,esTemplate方法避免TransportClient每次使用创建和释放</p>
*
* @author chenrui
* @since 2018-08-28
*/
@Configuration
@EnableElasticsearchRepositories(basePackages = "com.marsdl.dao.es")
@Component
public class EsConfig {
@Value("${spring.data.elasticsearch.cluster-name}")
private String clusterName;
private Client esClient;
@Bean
public Client client() {
TransportClient client = null;
try {
final Settings elasticsearchSettings = Settings.builder()
.put("client.transport.sniff", true)
.put("cluster.name", clusterName).build();
client = new PreBuiltTransportClient(elasticsearchSettings);
client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"), 9300));
} catch (Exception e) {
e.printStackTrace();
}
return client;
}
//避免TransportClient每次使用创建和释放
public Client esTemplate() {
if( StringUtils.isEmpty(esClient) || StringUtils.isEmpty(esClient.admin())) {
esClient = client();
return esClient;
}
return esClient;
}
}
注意:java代码中es调用的端口是9300,千万别搞错了,不然又出现扣脚的问题。
elasticsearch在application.yml配置文件是这样子的,在上一篇博客中提到,在java变量中获取yml内容。https://blog.csdn.net/Hello_Ray/article/details/82151731
spring:
data:
elasticsearch:
cluster-name: chenrui
cluster-nodes: localhost:9300
repositories:
#仓库中存储数据
enabled: true
下面是service使用esclient封装多字段查询,也可以放在dao层。
@Autowired
EsConfig esConfig;
/**
* <p>高亮查询内容, query的值查询两个字段name, director。当然了你可以配置查询更多个字段或者你可以改成你所需查询的字段</p>
* @param query
* @return List<FilmEntity> list
*/
public List<FilmEntity> search(String query) {
Client client = esConfig.esTemplate();
HighlightBuilder highlightBuilder = new HighlightBuilder();
//高亮显示规则
highlightBuilder.preTags("<span style='color:red'>");
highlightBuilder.postTags("</span>");
//指定高亮字段
highlightBuilder.field("name");
highlightBuilder.field("director");
//添加查询的字段内容
String [] fileds = {"name", "director"};
QueryBuilder matchQuery = QueryBuilders.multiMatchQuery(query, fileds);
//搜索数据
SearchResponse response = client.prepareSearch("chenrui")
.setQuery(matchQuery)
.highlighter(highlightBuilder)
.execute().actionGet();
SearchHits searchHits = response.getHits();
System.out.println("记录数-->"+searchHits.getTotalHits());
//List<String> list = new ArrayList<>();
List<FilmEntity> list = new ArrayList<>();
for(SearchHit hit : searchHits) {
FilmEntity entity = new FilmEntity();
Map<String, Object> entityMap = hit.getSourceAsMap();
//高亮字段
if(!StringUtils.isEmpty(hit.getHighlightFields().get("name"))) {
Text[] text = hit.getHighlightFields().get("name").getFragments();
entity.setName(text[0].toString());
entity.setDirector(String.valueOf(entityMap.get("director")));
}
if(!StringUtils.isEmpty(hit.getHighlightFields().get("director"))) {
Text[] text = hit.getHighlightFields().get("director").getFragments();
entity.setDirector(text[0].toString());
entity.setName(String.valueOf(entityMap.get("name")));
}
//map to object
if(!CollectionUtils.isEmpty(entityMap)) {
if(!StringUtils.isEmpty(entityMap.get("id"))) {
entity.setId(Long.valueOf(String.valueOf(entityMap.get("id"))));
}
if(!StringUtils.isEmpty(entityMap.get("language"))) {
entity.setLanguage(String.valueOf(entityMap.get("language")));
}
}
list.add(entity);
}
return list;
}
下面是我控制台出现的结果。
当然了你也可以拿这个client干很多事情,比如多字段,聚合,高亮。灵活度大大超过了
FilmDao extends ElasticsearchRepository<FilmEntity, Long>这种用法。而且这里的client只是第一次在启动创建,不会每次使用都创建然后释放,性能提高很多。
说句废话:elasticsearch有很多公司都在用,但是网上我没有看到一个简短小巧demo,这个是非常蛋疼的。不知道是不发出来,还是感觉发这种东西水平太低了。希望大家能将自己的感悟和新的东西分享出来,我认为这不会让自己少块肉。(自己baidu和google太痛苦了,发个牢骚)
该博客的源码地址在 https://gitee.com/cnhellorui/some_source_code/tree/master/fileserver
代码中肯定存在错误和需要优化和改进,希望大家能够指正或者email me: aaa@qq.com