import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.elasticsearch.core.ElasticsearchOperations; import org.springframework.data.elasticsearch.core.SearchHit; import org.springframework.data.elasticsearch.core.SearchHits; import org.springframework.data.elasticsearch.core.SearchPage; import org.springframework.data.elasticsearch.core.SearchScrollHits; import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.List; import java.util.Map; @Component public class YourElasticsearchService { private final ElasticsearchOperations elasticsearchOperations; @Autowired public YourElasticsearchService(ElasticsearchOperations elasticsearchOperations) { this.elasticsearchOperations = elasticsearchOperations; } public Page<YourEntity> searchWithHighlight(String keyword, int pageNumber, int pageSize) { NativeSearchQueryBuilder searchQuery = new NativeSearchQueryBuilder() .withQuery(QueryBuilders.matchQuery("field_name", keyword)) .withHighlightFields(new HighlightBuilder.Field("field_name").preTags("<span style='color:red'>").postTags("</span>")) .withPageable(PageRequest.of(pageNumber - 1, pageSize)); SearchPage<YourEntity> searchPage = elasticsearchOperations.search(searchQuery.build(), YourEntity.class); SearchHits<YourEntity> searchHits = searchPage.getSearchHits(); List<YourEntity> resultList = new ArrayList<>(); for (SearchHit<YourEntity> searchHit : searchHits) { YourEntity result = searchHit.getContent(); Map<String, List<String>> highlightFields = searchHit.getHighlightFields(); if (highlightFields.containsKey("field_name")) { List<String> highlightedContent = highlightFields.get("field_name"); if (!highlightedContent.isEmpty()) { result.setHighlightedContent(highlightedContent.get(0)); } } resultList.add(result); } return new PageImpl<>(resultList, searchPage.getPageable(), searchPage.getTotalHits()); } }
在上述代码中,YourElasticsearchService
是一个使用Spring Data Elasticsearch的示例服务类。在构造函数中,我们注入了ElasticsearchOperations
,用于获取ES的操作对象。
searchWithHighlight
方法接收关键词、页码和页面大小作为参数,并使用NativeSearchQueryBuilder
构建搜索查询。通过调用QueryBuilders.matchQuery
方法设置匹配查询的字段和关键词,并使用HighlightBuilder.Field
类设置需要高亮的字段和高亮标签。
然后,使用elasticsearchOperations.search
方法执行搜索查询,并使用SearchPage
对象接收搜索结果。
在遍历搜索结果时,我们提取实体对象,并检查是否存在高亮字段。如果存在,我们取出第一个高亮内容,将其设置到实体对象中的highlightedContent
属性中。
最后,使用PageImpl
类构建一个包含实体列表、分页信息和搜索结果总数的Page
对象,作为结果返回。
你可以根据需要,将其他逻辑和查询条件添加到代码中。
在前端页面中,可以使用适当的HTML渲染方式来将高亮内容显示为红色。例如,使用Thymeleaf模板引擎:
<p th:utext="${entity.highlightedContent}"></p>
查询条件如何带多个字段
可以使用Elasticsearch的多字段查询(multi-field query)来实现。以下是一个示例代码,展示如何在Spring Data Elasticsearch中使用多字段查询:
import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.elasticsearch.core.ElasticsearchOperations; import org.springframework.data.elasticsearch.core.SearchHit; import org.springframework.data.elasticsearch.core.SearchHits; import org.springframework.data.elasticsearch.core.SearchPage; import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.List; import java.util.Map; @Component public class YourElasticsearchService { private final ElasticsearchOperations elasticsearchOperations; @Autowired public YourElasticsearchService(ElasticsearchOperations elasticsearchOperations) { this.elasticsearchOperations = elasticsearchOperations; } public Page<YourEntity> searchWithHighlight(String keyword, int pageNumber, int pageSize) { NativeSearchQueryBuilder searchQuery = new NativeSearchQueryBuilder() .withQuery(QueryBuilders.multiMatchQuery(keyword, "field1", "field2", "field3")) .withHighlightFields( new HighlightBuilder.Field("field1").preTags("<span style='color:red'>").postTags("</span>"), new HighlightBuilder.Field("field2").preTags("<span style='color:red'>").postTags("</span>"), new HighlightBuilder.Field("field3").preTags("<span style='color:red'>").postTags("</span>") ) .withPageable(PageRequest.of(pageNumber - 1, pageSize)); SearchPage<YourEntity> searchPage = elasticsearchOperations.search(searchQuery.build(), YourEntity.class); SearchHits<YourEntity> searchHits = searchPage.getSearchHits(); List<YourEntity> resultList = new ArrayList<>(); for (SearchHit<YourEntity> searchHit : searchHits) { YourEntity result = searchHit.getContent(); Map<String, List<String>> highlightFields = searchHit.getHighlightFields(); if (highlightFields.containsKey("field1")) { List<String> highlightedContent = highlightFields.get("field1"); if (!highlightedContent.isEmpty()) { result.setHighlightedField1(highlightedContent.get(0)); } } if (highlightFields.containsKey("field2")) { List<String> highlightedContent = highlightFields.get("field2"); if (!highlightedContent.isEmpty()) { result.setHighlightedField2(highlightedContent.get(0)); } } if (highlightFields.containsKey("field3")) { List<String> highlightedContent = highlightFields.get("field3"); if (!highlightedContent.isEmpty()) { result.setHighlightedField3(highlightedContent.get(0)); } } resultList.add(result); } return new PageImpl<>(resultList, searchPage.getPageable(), searchPage.getTotalHits()); } }
在上述代码中,我们使用QueryBuilders.multiMatchQuery
方法来创建一个多字段查询,指定了要在field1
、field2
和field3
中进行搜索匹配。
通过调用withHighlightFields
方法,我们将每个字段都配置为需要高亮显示,使用相应的高亮标签。
在搜索结果的遍历中,对每个字段执行类似的处理,检查是否存在高亮字段,如果存在,取出第一个高亮内容,并将其设置到对应的实体属性中。
最后,使用PageImpl
构造一个Page
对象,包含实体列表、分页信息和搜索结果总数。
条件能加个必须的字段
在搜索条件中同时包含多个字段查询和必须的字段查询,可以使用Elasticsearch的布尔查询来实现。以下是修改后的示例代码
import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.elasticsearch.core.ElasticsearchOperations; import org.springframework.data.elasticsearch.core.SearchHit; import org.springframework.data.elasticsearch.core.SearchHits; import org.springframework.data.elasticsearch.core.SearchPage; import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.List; import java.util.Map; @Component public class YourElasticsearchService { private final ElasticsearchOperations elasticsearchOperations; @Autowired public YourElasticsearchService(ElasticsearchOperations elasticsearchOperations) { this.elasticsearchOperations = elasticsearchOperations; } public Page<YourEntity> searchWithHighlight(String keyword, String requiredField, int pageNumber, int pageSize) { BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery() .must(QueryBuilders.multiMatchQuery(keyword, "field1", "field2", "field3")) .must(QueryBuilders.matchQuery("required_field", requiredField)); NativeSearchQueryBuilder searchQuery = new NativeSearchQueryBuilder() .withQuery(boolQueryBuilder) .withHighlightFields( new HighlightBuilder.Field("field1").preTags("<span style='color:red'>").postTags("</span>"), new HighlightBuilder.Field("field2").preTags("<span style='color:red'>").postTags("</span>"), new HighlightBuilder.Field("field3").preTags("<span style='color:red'>").postTags("</span>") ) .withPageable(PageRequest.of(pageNumber - 1, pageSize)); SearchPage<YourEntity> searchPage = elasticsearchOperations.search(searchQuery.build(), YourEntity.class); SearchHits<YourEntity> searchHits = searchPage.getSearchHits(); List<YourEntity> resultList = new ArrayList<>(); for (SearchHit<YourEntity> searchHit : searchHits) { YourEntity result = searchHit.getContent(); Map<String, List<String>> highlightFields = searchHit.getHighlightFields(); if (highlightFields.containsKey("field1")) { List<String> highlightedContent = highlightFields.get("field1"); if (!highlightedContent.isEmpty()) { result.setHighlightedField1(highlightedContent.get(0)); } } if (highlightFields.containsKey("field2")) { List<String> highlightedContent = highlightFields.get("field2"); if (!highlightedContent.isEmpty()) { result.setHighlightedField2(highlightedContent.get(0)); } } if (highlightFields.containsKey("field3")) { List<String> highlightedContent = highlightFields.get("field3"); if (!highlightedContent.isEmpty()) { result.setHighlightedField3(highlightedContent.get(0)); } } resultList.add(result); } return new PageImpl<>(resultList, searchPage.getPageable(), searchPage.getTotalHits()); } }
在上述代码中,我们创建了一个BoolQueryBuilder
对象,并使用must
方法将多字段查询和必须的字段查询都添加到其中。
然后,在NativeSearchQueryBuilder
中使用withQuery
方法将这个布尔查询作为搜索查询的一部分。
最后,搜索结果的遍历和高亮处理方式与之前的示例相同。
请注意,在创建布尔查询时,你还可以使用其他方法,如should
(或者shoulds
)来添加可选的字段查询,或者使用mustNot
方法来添加排除条件