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方法来创建一个多字段查询,指定了要在field1field2field3中进行搜索匹配。

通过调用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方法来添加排除条件