java 搜索引擎 关键词高亮_和我一起打造个简单搜索之SpringDataElasticSearch关键词高亮(示例代码)...

前面几篇文章详细讲解了 ElasticSearch 的搭建以及使用 SpringDataElasticSearch 来完成搜索查询,但是搜索一般都会有搜索关键字高亮的功能,今天我们把它给加上。

系列文章

环境依赖

本文以及后续 es 系列文章都基于 5.5.3 这个版本的 elasticsearch ,这个版本比较稳定,可以用于生产环境。

SpringDataElasticSearch 的基本使用可以看我的上一篇文章 和我一起打造个简单搜索之SpringDataElasticSearch入门,本文就不再赘述。

高亮关键字实现

前文查询是通过写一个接口来继承 ElasticsearchRepository 来实现的,但是如果要实现高亮,我们就不能这样做了,我们需要使用到 ElasticsearchTemplate来完成。

查看这个类的源码

public class ElasticsearchTemplate implements ElasticsearchOperations, ApplicationContextAware {

...

}

可以看到,ElasticsearchTemplate 实现了接口 ApplicationContextAware,所以这个类是被 Spring 管理的,可以在类里面直接注入使用。

代码如下:

@Slf4j

@Component

public class HighlightBookRepositoryTest extends EsSearchApplicationTests {

@Autowired

private ElasticsearchTemplate elasticsearchTemplate;

@Resource

private ExtResultMapper extResultMapper;

@Test

public void testHighlightQuery() {

BookQuery query = new BookQuery();

query.setQueryString("穿越");

// 复合查询

BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

// 以下为查询条件, 使用 must query 进行查询组合

MultiMatchQueryBuilder matchQuery = QueryBuilders.multiMatchQuery(query.getQueryString(), "name", "intro", "author");

boolQuery.must(matchQuery);

PageRequest pageRequest = PageRequest.of(query.getPage() - 1, query.getSize());

NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()

.withQuery(boolQuery)

.withHighlightFields(

new HighlightBuilder.Field("name").preTags("").postTags(""),

new HighlightBuilder.Field("author").preTags("").postTags(""))

.withPageable(pageRequest)

.build();

Page books = elasticsearchTemplate.queryForPage(searchQuery, Book.class, extResultMapper);

books.forEach(e -> log.info("{}", e));

// 穿越小道人

}

}

注意这里 的

Page books = elasticsearchTemplate.queryForPage(searchQuery, Book.class, extResultMapper);

这里返回的是分页对象。

查询方式和上文的差不多,只不过是是 Repository 变成了 ElasticsearchTemplate,操作方式也大同小异。

这里用到了 ExtResultMapper,请接着看下文。

自定义ResultMapper

ResultMapper 是用于将 ES 文档转换成 Java 对象的映射类,因为 SpringDataElasticSearch 默认的的映射类 DefaultResultMapper 不支持高亮,因此,我们需要自己定义一个 ResultMapper。

复制 DefaultResultMapper 类,重命名为 ExtResultMapper,对构造方法名称修改为正确的值。

新增一个方法,用于将高亮的内容赋值给需要转换的 Java 对象内。

在 mapResults 方法内调用这个方法。

注意:这个类可以直接拷贝到你的项目中直接使用!

我写这么多,只是想说明为什么这个类是这样的。

import com.fasterxml.jackson.core.JsonEncoding;

import com.fasterxml.jackson.core.JsonFactory;

import com.fasterxml.jackson.core.JsonGenerator;

import org.apache.commons.beanutils.PropertyUtils;

import org.elasticsearch.action.get.GetResponse;

import org.elasticsearch.action.get.MultiGetItemResponse;

import org.elasticsearch.action.get.MultiGetResponse;

import org.elasticsearch.action.search.SearchResponse;

import org.elasticsearch.common.text.Text;

import org.elasticsearch.search.SearchHit;

import org.elasticsearch.search.SearchHitField;

import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;

import org.springframework.data.domain.Pageable;

import org.springframework.data.elasticsearch.ElasticsearchException;

import org.springframework.data.elasticsearch.annotations.Document;

import org.springframework.data.elasticsearch.annotations.ScriptedField;

import org.springframework.data.elasticsearch.core.AbstractResultMapper;

import org.springframework.data.elasticsearch.core.DefaultEntityMapper;

import org.springframework.data.elasticsearch.core.EntityMapper;

import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;

import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;

import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;

import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;

import org.springframework.data.mapping.context.MappingContext;

import org.springframework.stereotype.Component;

import org.springframework.util.Assert;

import org.springframework.util.StringUtils;

import java.io.ByteArrayOutputStream;

import java.io.IOException;

import java.lang.reflect.InvocationTargetException;

import java.nio.charset.Charset;

import java.util.*;

/**

* 类名称:ExtResultMapper

* 类描述:自定义结果映射类

* 创建人:WeJan

* 创建时间:2018-09-13 20:47

*/

@Component

public class ExtResultMapper extends AbstractResultMapper {

private MappingContext extends ElasticsearchPersistentEntity>, ElasticsearchPersistentProperty> mappingContext;

public ExtResultMapper() {

super(new DefaultEntityMapper());

}

public ExtResultMapper(MappingContext extends ElasticsearchPersistentEntity>, ElasticsearchPersistentProperty> mappingContext) {

super(new DefaultEntityMapper());

this.mappingContext = mappingContext;

}

public ExtResultMapper(EntityMapper entityMapper) {

super(entityMapper);

}

public ExtResultMapper(

MappingContext extends ElasticsearchPersistentEntity>, ElasticsearchPersistentProperty> mappingContext,

EntityMapper entityMapper) {

super(entityMapper);

this.mappingContext = mappingContext;

}

@Override

public AggregatedPage mapResults(SearchResponse response, Class clazz, Pageable pageable) {

long totalHits = response.getHits().totalHits();

List results = new ArrayList<>();

for (SearchHit hit : response.getHits()) {

if (hit != null) {

T result = null;

if (StringUtils.hasText(hit.sourceAsString())) {

result = mapEntity(hit.sourceAsString(), clazz);

} else {

result = mapEntity(hit.getFields().values(), clazz);

}

setPersistentEntityId(result, hit.getId(), clazz);

setPersistentEntityVersion(result, hit.getVersion(), clazz);

populateScriptFields(result, hit);

// 高亮查询

populateHighLightedFields(result, hit.getHighlightFields());

results.add(result);

}

}

return new AggregatedPageImpl(results, pageable, totalHits, response.getAggregations(), response.getScrollId());

}

private void populateHighLightedFields(T result, Map highlightFields) {

for (HighlightField field : highlightFields.values()) {

try {

PropertyUtils.setProperty(result, field.getName(), concat(field.fragments()));

} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {

throw new ElasticsearchException("failed to set highlighted value for field: " + field.getName()

+ " with value: " + Arrays.toString(field.getFragments()), e);

}

}

}

private String concat(Text[] texts) {

StringBuffer sb = new StringBuffer();

for (Text text : texts) {

sb.append(text.toString());

}

return sb.toString();

}

private void populateScriptFields(T result, SearchHit hit) {

if (hit.getFields() != null && !hit.getFields().isEmpty() && result != null) {

for (java.lang.reflect.Field field : result.getClass().getDeclaredFields()) {

ScriptedField scriptedField = field.getAnnotation(ScriptedField.class);

if (scriptedField != null) {

String name = scriptedField.name().isEmpty() ? field.getName() : scriptedField.name();

SearchHitField searchHitField = hit.getFields().get(name);

if (searchHitField != null) {

field.setAccessible(true);

try {

field.set(result, searchHitField.getValue());

} catch (IllegalArgumentException e) {

throw new ElasticsearchException("failed to set scripted field: " + name + " with value: "

+ searchHitField.getValue(), e);

} catch (IllegalAccessException e) {

throw new ElasticsearchException("failed to access scripted field: " + name, e);

}

}

}

}

}

}

private T mapEntity(Collection values, Class clazz) {

return mapEntity(buildJSONFromFields(values), clazz);

}

private String buildJSONFromFields(Collection values) {

JsonFactory nodeFactory = new JsonFactory();

try {

ByteArrayOutputStream stream = new ByteArrayOutputStream();

JsonGenerator generator = nodeFactory.createGenerator(stream, JsonEncoding.UTF8);

generator.writeStartObject();

for (SearchHitField value : values) {

if (value.getValues().size() > 1) {

generator.writeArrayFieldStart(value.getName());

for (Object val : value.getValues()) {

generator.writeObject(val);

}

generator.writeEndArray();

} else {

generator.writeObjectField(value.getName(), value.getValue());

}

}

generator.writeEndObject();

generator.flush();

return new String(stream.toByteArray(), Charset.forName("UTF-8"));

} catch (IOException e) {

return null;

}

}

@Override

public T mapResult(GetResponse response, Class clazz) {

T result = mapEntity(response.getSourceAsString(), clazz);

if (result != null) {

setPersistentEntityId(result, response.getId(), clazz);

setPersistentEntityVersion(result, response.getVersion(), clazz);

}

return result;

}

@Override

public LinkedList mapResults(MultiGetResponse responses, Class clazz) {

LinkedList list = new LinkedList<>();

for (MultiGetItemResponse response : responses.getResponses()) {

if (!response.isFailed() && response.getResponse().isExists()) {

T result = mapEntity(response.getResponse().getSourceAsString(), clazz);

setPersistentEntityId(result, response.getResponse().getId(), clazz);

setPersistentEntityVersion(result, response.getResponse().getVersion(), clazz);

list.add(result);

}

}

return list;

}

private void setPersistentEntityId(T result, String id, Class clazz) {

if (mappingContext != null && clazz.isAnnotationPresent(Document.class)) {

ElasticsearchPersistentEntity> persistentEntity = mappingContext.getRequiredPersistentEntity(clazz);

ElasticsearchPersistentProperty idProperty = persistentEntity.getIdProperty();

// Only deal with String because ES generated Ids are strings !

if (idProperty != null && idProperty.getType().isAssignableFrom(String.class)) {

persistentEntity.getPropertyAccessor(result).setProperty(idProperty, id);

}

}

}

private void setPersistentEntityVersion(T result, long version, Class clazz) {

if (mappingContext != null && clazz.isAnnotationPresent(Document.class)) {

ElasticsearchPersistentEntity> persistentEntity = mappingContext.getPersistentEntity(clazz);

ElasticsearchPersistentProperty versionProperty = persistentEntity.getVersionProperty();

// Only deal with Long because ES versions are longs !

if (versionProperty != null && versionProperty.getType().isAssignableFrom(Long.class)) {

// check that a version was actually returned in the response, -1 would indicate that

// a search didn‘t request the version ids in the response, which would be an issue

Assert.isTrue(version != -1, "Version in response is -1");

persistentEntity.getPropertyAccessor(result).setProperty(versionProperty, version);

}

}

}

}

注意这里使用到了 PropertyUtils ,需要引入一个 Apache 的依赖。

commons-beanutils

commons-beanutils

1.9.3

自定义 ResultMapper 写好之后,添加 @Component 注解,表示为 Spring 的一个组件,在类中进行注入使用即可。

最后

本文示例项目地址:https://github.com/Mosiki/SpringDataElasticSearchQuickStartExample

有疑问?

欢迎来信,给我写信

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/567232.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python词汇网络分析_8个Python高效数据分析的技巧!

文章来源于网络来源&#xff5c;CSDN这篇文章介绍了8个使用Python进行数据分析的方法&#xff0c;不仅能够提升运行效率&#xff0c;还能够使代码更加“优美”。定义某种列表时&#xff0c;写For 循环过于麻烦&#xff0c;幸运的是&#xff0c;Python有一种内置的方法可以在一行…

改变php二维数组的值_php如何修改二维数组中的值?

php修改二维数组中值的方法&#xff1a;1、通过【for($i 0; $i < count(Array()); $i)】语句修改&#xff1b;2、通过foreach($users as &$user)语句修改。php修改二维数组中值的方法&#xff1a;1、二维数组可以通过for($i 0; $i < count(Array()); $i)这种形式…

华为python673集_python库-collections模块Counter类

Counter类主要是用来跟踪值出现的次数。它是一个无序的容器类型&#xff0c;以字典的键值对形式存储&#xff0c;其中元素作为key&#xff0c;其计数作为value。demo:all_words [] # 列表里面是汉字(可重复)counter Counter(all_words) # 返回 dict Counter类的目的是用来跟踪…

php下载 微信头像图片_php保存微信用户头像到本地或者服务器的完美方案!

//方法一&#xff1a;//推荐用该方法$header array(User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:45.0) Gecko/20100101 Firefox/45.0,Accept-Language: zh-CN,zh;q0.8,en-US;q0.5,en;q0.3,Accept-Encoding: gzip, deflate,);$urlhttp://wx.qlogo.cn/mmopen/vi_3…

php 预防循环发短信_php短信接口发送短信失败,罪魁祸首原来在这里

随着国民经济的高速发展&#xff0c;短信的应用也逐渐商业化&#xff0c;很多企业、商家开始使用php短信接口来进行推广、营销或内部管理&#xff0c;只是个别的商家在应用php短信接口的时候却遇到了短信发送失败的现象&#xff0c;这到底是因为商家操作有误&#xff0c;还是因…

python实时显示温度变化_python实时温度绘制

我目前正在做一个项目&#xff0c;它需要实时监控各种量&#xff0c;如温度、压力、湿度等。我正在采用一种方法&#xff0c;即使用matplotlib和drwnow绘制一个图形。在HOST "localhost"PORT 4223UID1 "tsJ" # S1from tinkerforge.ip_connection import…

jlabel字怎么变化_怎样才能把字写好?详解最科学的练字方法及步骤

一&#xff1a;正确的学习之路1&#xff1a;临帖从古到今&#xff0c;临帖是学习书法最基本的方法&#xff0c;没有一个书法家是没有临过帖的。2&#xff1a;专注专注学一个书法家的字&#xff0c;专心致志&#xff0c;认真临写&#xff0c;持之以恒&#xff0c;直到形同神似。…

php取指定文件夹图片大小,php获取某文件夹的大小

// 获取文件夹大小function getDirSize($dir){$handle opendir($dir);while (false!($FolderOrFile readdir($handle))){if($FolderOrFile ! "." && $FolderOrFile ! ".."){if(is_dir("$dir/$FolderOrFile")){$sizeResult getDirSize…

python lncrna_lncRNA分析

目前人们对lncRNA认识还处在初级阶段&#xff0c;lncRNA起初被认为是基因组转录的“噪音”&#xff0c;是RNA聚合酶II转录的副产物&#xff0c;不具有生物学功能。然而大量研究表明&#xff0c;lncRNA在细胞核内、核外&#xff0c;通过染色质修饰&#xff0c;转录调控&#xff…

ubuntu 缺少php安装包,ubuntu 16.04 lts安装php环境和composer依赖包管理

安装环境1.安装之前先更新系统1 sudo apt update2.安装Apache21 sudo apt install apache23.安装PHP1 sudo apt install php2 sudo apt-get install libapache2-mod-php4.安装MySQL1 sudo apt install mysql-server php7.0-mysql2 sudo apt-get install mysql-client3 mysql_se…

找不到具有指定id_JAVA如何整合es指定字段不分词搜索?

一、问题在做一个需求的时候&#xff0c;需要按照电话号码查询用户关系&#xff0c;所以我这边先讲相关信息同步到es&#xff0c;但是电话号码是加密的&#xff0c;所以显示的字符串是杂乱的&#xff0c;既有字母&#xff0c;又有斜杠等号等字符&#xff0c;在进行分词查询的时…

php 解析java map,java_java遍历Map的几种方法分析,本文实例分析了java遍历Map的几 - phpStudy...

java遍历Map的几种方法分析本文实例分析了java遍历Map的几种方法。分享给大家供大家参考&#xff0c;具体如下&#xff1a;Java代码&#xff1a;Map mapnew HashMap();map.put("username", "qq");map.put("passWord", "123");map.put(…

vant 项目_vueCli4+vant+router+vuex+移动端适配

教程的开始 各位同学请先安装node、npm、vuecli4 巴拉巴拉...废话不多说立即开始随便找个地方 cmd&#xff0c;powerShell也可以 命令行走起1. 创建vue项目 随便起个名字就叫my-vue吧&#xff0c;傻瓜式回车键就行&#xff0c;也可以根据自己的喜好选择eslint。vue create my-v…

php快废了,PHP为什么要逐渐废弃一些函数?出于什么原因呢?

1.比如说废弃mysql开头的几个数据库函数&#xff0c;是为了面向对象而废弃的么&#xff1f;2.还有magic_quotes这个为什么又要去掉呢?3.等等。。知道的说一下&#xff0c;感激不尽。回复内容&#xff1a;1.比如说废弃mysql开头的几个数据库函数&#xff0c;是为了面向对象而废…

电路结构原理_精密半波、全波整流电路结构原理图解

利用二极管(开关器件)的单向导电特性&#xff0c;和放大器的优良放大性能相结合&#xff0c;可做到对输入交变信号(尤其是小幅度的电压信号)进行精密的整流&#xff0c;由此构成精密半波整流电路。若由此再添加简单电路&#xff0c;即可构成精密全波整流电路。二极管的导通压降…

java记录pv,计算500万PV/每天的网站

Refer to:http://elf8848.iteye.com/blog/967049你想建设一个能承受500万PV/每天的网站吗&#xff1f; 500万PV是什么概念&#xff1f;服务器每秒要处理多少个请求才能应对&#xff1f;如果计算呢&#xff1f;PV是什么&#xff1a;PV是page view的简写。PV是指页面的访问次数&a…

vlookup两个条件匹配_vlookup,你还是只会基础的单条件查找?

很多数据分析师在简历上都会说自己熟悉excel&#xff0c;但熟练使用excel必须要掌握的核心函数vlookup首当其冲&#xff0c;其次是sumifs、match、index等等。就算熟悉vlookup&#xff0c;很多人也只会基础的单条件查找&#xff0c;其实在工作中能用到vlookup的地方有很多&…

matlab 绘制符号函数,DAY8 MATLAB学习笔记—simulink入门、MATLAB符号函数的图形绘制...

如何打开simulink&#xff1a;启动simulink&#xff1a;先打开MATLAB软件界面第一步打开simulink第二步在command windows输入 simulink然后enter&#xff0c;等待有很多模块库第三步&#xff1a;常用的simulink库打开以后会看到simulink library browser这个界面最常用的就是s…

后盾网php多少钱_商标转让做公证花多少钱-购店网

当别人对您的商标感兴趣&#xff0c;或者您想买属于自己的商标时&#xff0c;转让商标是个好主意。但不了解商标转让流程的朋友&#xff0c;可能会担心自己对商标公证书的作用缺乏了解。所以今天我们给大家介绍一下什么是商标转让公证的用途&#xff0c;具体什么是商标公证。想…

MATLAB求线性代数的参数范围,MATLAB科学计算04(线性代数问题求解一)

文章目录特殊矩阵矩阵的基本概念求解线性方程组直接求解判定求解特殊矩阵零矩阵、1矩阵及单位矩阵生成nxn方阵&#xff1a;Azeros(n), Bones(n), Ceye(n)生成mxn矩阵&#xff1a;Azeros(m,n), Bones(m,n), Ceye(m,n)生成和矩阵B同样位数的矩阵&#xff1a;Azeros(size(B))**生成…