日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 前端技术 > javascript >内容正文

javascript

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

發(fā)布時(shí)間:2025/3/15 javascript 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java 搜索引擎 关键词高亮_和我一起打造个简单搜索之SpringDataElasticSearch关键词高亮(示例代码)... 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前面幾篇文章詳細(xì)講解了 ElasticSearch 的搭建以及使用 SpringDataElasticSearch 來完成搜索查詢,但是搜索一般都會(huì)有搜索關(guān)鍵字高亮的功能,今天我們把它給加上。

系列文章

環(huán)境依賴

本文以及后續(xù) es 系列文章都基于 5.5.3 這個(gè)版本的 elasticsearch ,這個(gè)版本比較穩(wěn)定,可以用于生產(chǎn)環(huán)境。

SpringDataElasticSearch 的基本使用可以看我的上一篇文章 和我一起打造個(gè)簡(jiǎn)單搜索之SpringDataElasticSearch入門,本文就不再贅述。

高亮關(guān)鍵字實(shí)現(xiàn)

前文查詢是通過寫一個(gè)接口來繼承 ElasticsearchRepository 來實(shí)現(xiàn)的,但是如果要實(shí)現(xiàn)高亮,我們就不能這樣做了,我們需要使用到 ElasticsearchTemplate來完成。

查看這個(gè)類的源碼

public class ElasticsearchTemplate implements ElasticsearchOperations, ApplicationContextAware {

...

}

可以看到,ElasticsearchTemplate 實(shí)現(xiàn)了接口 ApplicationContextAware,所以這個(gè)類是被 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("穿越");

// 復(fù)合查詢

BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

// 以下為查詢條件, 使用 must query 進(jìn)行查詢組合

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);

這里返回的是分頁(yè)對(duì)象。

查詢方式和上文的差不多,只不過是是 Repository 變成了 ElasticsearchTemplate,操作方式也大同小異。

這里用到了 ExtResultMapper,請(qǐng)接著看下文。

自定義ResultMapper

ResultMapper 是用于將 ES 文檔轉(zhuǎn)換成 Java 對(duì)象的映射類,因?yàn)?SpringDataElasticSearch 默認(rèn)的的映射類 DefaultResultMapper 不支持高亮,因此,我們需要自己定義一個(gè) ResultMapper。

復(fù)制 DefaultResultMapper 類,重命名為 ExtResultMapper,對(duì)構(gòu)造方法名稱修改為正確的值。

新增一個(gè)方法,用于將高亮的內(nèi)容賦值給需要轉(zhuǎn)換的 Java 對(duì)象內(nèi)。

在 mapResults 方法內(nèi)調(diào)用這個(gè)方法。

注意:這個(gè)類可以直接拷貝到你的項(xiàng)目中直接使用!

我寫這么多,只是想說明為什么這個(gè)類是這樣的。

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

* 類描述:自定義結(jié)果映射類

* 創(chuàng)建人:WeJan

* 創(chuàng)建時(shí)間: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 ,需要引入一個(gè) Apache 的依賴。

commons-beanutils

commons-beanutils

1.9.3

自定義 ResultMapper 寫好之后,添加 @Component 注解,表示為 Spring 的一個(gè)組件,在類中進(jìn)行注入使用即可。

最后

本文示例項(xiàng)目地址:https://github.com/Mosiki/SpringDataElasticSearchQuickStartExample

有疑問?

歡迎來信,給我寫信

總結(jié)

以上是生活随笔為你收集整理的java 搜索引擎 关键词高亮_和我一起打造个简单搜索之SpringDataElasticSearch关键词高亮(示例代码)...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。