mirror of
https://github.com/spring-projects/spring-data-elasticsearch.git
synced 2025-06-29 07:12:26 +00:00
Support highlight query in @HighlightParameters annotation.
Original Pull Request #2802
This commit is contained in:
parent
d0ed80dfde
commit
96b38652ab
@ -21,6 +21,7 @@ import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Haibo Liu
|
||||
* @since 4.0
|
||||
*/
|
||||
@Documented
|
||||
@ -59,6 +60,8 @@ public @interface HighlightParameters {
|
||||
|
||||
int numberOfFragments() default -1;
|
||||
|
||||
Query highlightQuery() default @Query;
|
||||
|
||||
String order() default "";
|
||||
|
||||
int phraseLimit() default -1;
|
||||
|
@ -15,14 +15,13 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core.query.highlight;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Haibo Liu
|
||||
* @since 4.3
|
||||
*/
|
||||
public class Highlight {
|
||||
@ -57,42 +56,4 @@ public class Highlight {
|
||||
public List<HighlightField> getFields() {
|
||||
return fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Highlight} from an Annotation instance.
|
||||
*
|
||||
* @param highlight must not be {@literal null}
|
||||
* @return highlight definition
|
||||
*/
|
||||
public static Highlight of(org.springframework.data.elasticsearch.annotations.Highlight highlight) {
|
||||
|
||||
Assert.notNull(highlight, "highlight must not be null");
|
||||
|
||||
org.springframework.data.elasticsearch.annotations.HighlightParameters parameters = highlight.parameters();
|
||||
HighlightParameters highlightParameters = HighlightParameters.builder() //
|
||||
.withBoundaryChars(parameters.boundaryChars()) //
|
||||
.withBoundaryMaxScan(parameters.boundaryMaxScan()) //
|
||||
.withBoundaryScanner(parameters.boundaryScanner()) //
|
||||
.withBoundaryScannerLocale(parameters.boundaryScannerLocale()) //
|
||||
.withEncoder(parameters.encoder()) //
|
||||
.withForceSource(parameters.forceSource()) //
|
||||
.withFragmenter(parameters.fragmenter()) //
|
||||
.withFragmentSize(parameters.fragmentSize()) //
|
||||
.withNoMatchSize(parameters.noMatchSize()) //
|
||||
.withNumberOfFragments(parameters.numberOfFragments()) //
|
||||
.withOrder(parameters.order()) //
|
||||
.withPhraseLimit(parameters.phraseLimit()) //
|
||||
.withPreTags(parameters.preTags()) //
|
||||
.withPostTags(parameters.postTags()) //
|
||||
.withRequireFieldMatch(parameters.requireFieldMatch()) //
|
||||
.withTagsSchema(parameters.tagsSchema()) //
|
||||
.withType(parameters.type()) //
|
||||
.build();
|
||||
|
||||
List<HighlightField> highlightFields = Arrays.stream(highlight.fields()) //
|
||||
.map(HighlightField::of) //
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return new Highlight(highlightParameters, highlightFields);
|
||||
}
|
||||
}
|
||||
|
@ -199,7 +199,7 @@ public abstract class HighlightCommonParameters {
|
||||
return (SELF) this;
|
||||
}
|
||||
|
||||
public SELF withHighlightQuery(Query highlightQuery) {
|
||||
public SELF withHighlightQuery(@Nullable Query highlightQuery) {
|
||||
this.highlightQuery = highlightQuery;
|
||||
return (SELF) this;
|
||||
}
|
||||
|
@ -15,14 +15,6 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.repository.query;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.elasticsearch.annotations.Highlight;
|
||||
@ -50,12 +42,19 @@ import org.springframework.data.repository.query.Parameters;
|
||||
import org.springframework.data.repository.query.QueryMethod;
|
||||
import org.springframework.data.repository.util.QueryExecutionConverters;
|
||||
import org.springframework.data.repository.util.ReactiveWrapperConverters;
|
||||
import org.springframework.data.util.Lazy;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* ElasticsearchQueryMethod
|
||||
*
|
||||
@ -66,6 +65,7 @@ import org.springframework.util.ClassUtils;
|
||||
* @author Christoph Strobl
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Alexander Torres
|
||||
* @author Haibo Liu
|
||||
*/
|
||||
public class ElasticsearchQueryMethod extends QueryMethod {
|
||||
|
||||
@ -81,8 +81,6 @@ public class ElasticsearchQueryMethod extends QueryMethod {
|
||||
@Nullable private ElasticsearchEntityMetadata<?> metadata;
|
||||
@Nullable private final Query queryAnnotation;
|
||||
@Nullable private final Highlight highlightAnnotation;
|
||||
private final Lazy<HighlightQuery> highlightQueryLazy = Lazy.of(this::createAnnotatedHighlightQuery);
|
||||
|
||||
@Nullable private final SourceFilters sourceFilters;
|
||||
|
||||
public ElasticsearchQueryMethod(Method method, RepositoryMetadata repositoryMetadata, ProjectionFactory factory,
|
||||
@ -143,19 +141,13 @@ public class ElasticsearchQueryMethod extends QueryMethod {
|
||||
* @throws IllegalArgumentException if no {@link Highlight} annotation is present on the method
|
||||
* @see #hasAnnotatedHighlight()
|
||||
*/
|
||||
public HighlightQuery getAnnotatedHighlightQuery() {
|
||||
public HighlightQuery getAnnotatedHighlightQuery(HighlightConverter highlightConverter) {
|
||||
|
||||
Assert.isTrue(hasAnnotatedHighlight(), "no Highlight annotation present on " + getName());
|
||||
|
||||
return highlightQueryLazy.get();
|
||||
}
|
||||
|
||||
private HighlightQuery createAnnotatedHighlightQuery() {
|
||||
|
||||
Assert.notNull(highlightAnnotation, "highlightAnnotation must not be null");
|
||||
|
||||
return new HighlightQuery(
|
||||
org.springframework.data.elasticsearch.core.query.highlight.Highlight.of(highlightAnnotation),
|
||||
highlightConverter.convert(highlightAnnotation),
|
||||
getDomainClass());
|
||||
}
|
||||
|
||||
@ -378,7 +370,7 @@ public class ElasticsearchQueryMethod extends QueryMethod {
|
||||
ElasticsearchConverter elasticsearchConverter) {
|
||||
|
||||
if (hasAnnotatedHighlight()) {
|
||||
query.setHighlightQuery(getAnnotatedHighlightQuery());
|
||||
query.setHighlightQuery(getAnnotatedHighlightQuery(new HighlightConverter(parameterAccessor, elasticsearchConverter)));
|
||||
}
|
||||
|
||||
var sourceFilter = getSourceFilter(parameterAccessor, elasticsearchConverter);
|
||||
|
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2013-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.repository.query;
|
||||
|
||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
|
||||
import org.springframework.data.elasticsearch.core.query.Query;
|
||||
import org.springframework.data.elasticsearch.core.query.StringQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.highlight.Highlight;
|
||||
import org.springframework.data.elasticsearch.core.query.highlight.HighlightField;
|
||||
import org.springframework.data.elasticsearch.core.query.highlight.HighlightParameters;
|
||||
import org.springframework.data.elasticsearch.repository.support.StringQueryUtil;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Convert {@link org.springframework.data.elasticsearch.annotations.Highlight} to {@link Highlight}.
|
||||
*
|
||||
* @author Haibo Liu
|
||||
*/
|
||||
public class HighlightConverter {
|
||||
|
||||
private final ElasticsearchParametersParameterAccessor parameterAccessor;
|
||||
private final ElasticsearchConverter elasticsearchConverter;
|
||||
|
||||
HighlightConverter(ElasticsearchParametersParameterAccessor parameterAccessor,
|
||||
ElasticsearchConverter elasticsearchConverter) {
|
||||
this.parameterAccessor = parameterAccessor;
|
||||
this.elasticsearchConverter = elasticsearchConverter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Highlight} from an Annotation instance.
|
||||
*
|
||||
* @param highlight must not be {@literal null}
|
||||
* @return highlight definition
|
||||
*/
|
||||
Highlight convert(org.springframework.data.elasticsearch.annotations.Highlight highlight) {
|
||||
|
||||
Assert.notNull(highlight, "highlight must not be null");
|
||||
|
||||
org.springframework.data.elasticsearch.annotations.HighlightParameters parameters = highlight.parameters();
|
||||
|
||||
// replace placeholders in highlight query with actual parameters
|
||||
Query highlightQuery = null;
|
||||
if (!parameters.highlightQuery().value().isEmpty()) {
|
||||
String rawString = parameters.highlightQuery().value();
|
||||
String queryString = new StringQueryUtil(elasticsearchConverter.getConversionService())
|
||||
.replacePlaceholders(rawString, parameterAccessor);
|
||||
highlightQuery = new StringQuery(queryString);
|
||||
}
|
||||
|
||||
HighlightParameters highlightParameters = HighlightParameters.builder() //
|
||||
.withBoundaryChars(parameters.boundaryChars()) //
|
||||
.withBoundaryMaxScan(parameters.boundaryMaxScan()) //
|
||||
.withBoundaryScanner(parameters.boundaryScanner()) //
|
||||
.withBoundaryScannerLocale(parameters.boundaryScannerLocale()) //
|
||||
.withEncoder(parameters.encoder()) //
|
||||
.withForceSource(parameters.forceSource()) //
|
||||
.withFragmenter(parameters.fragmenter()) //
|
||||
.withFragmentSize(parameters.fragmentSize()) //
|
||||
.withNoMatchSize(parameters.noMatchSize()) //
|
||||
.withNumberOfFragments(parameters.numberOfFragments()) //
|
||||
.withHighlightQuery(highlightQuery) //
|
||||
.withOrder(parameters.order()) //
|
||||
.withPhraseLimit(parameters.phraseLimit()) //
|
||||
.withPreTags(parameters.preTags()) //
|
||||
.withPostTags(parameters.postTags()) //
|
||||
.withRequireFieldMatch(parameters.requireFieldMatch()) //
|
||||
.withTagsSchema(parameters.tagsSchema()) //
|
||||
.withType(parameters.type()) //
|
||||
.build();
|
||||
|
||||
List<HighlightField> highlightFields = Arrays.stream(highlight.fields()) //
|
||||
.map(HighlightField::of) //
|
||||
.toList();
|
||||
|
||||
return new Highlight(highlightParameters, highlightFields);
|
||||
}
|
||||
}
|
@ -32,7 +32,6 @@ import java.util.stream.Stream;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.DisabledIf;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.Version;
|
||||
@ -46,9 +45,9 @@ import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.annotations.Field;
|
||||
import org.springframework.data.elasticsearch.annotations.Highlight;
|
||||
import org.springframework.data.elasticsearch.annotations.HighlightField;
|
||||
import org.springframework.data.elasticsearch.annotations.HighlightParameters;
|
||||
import org.springframework.data.elasticsearch.annotations.Query;
|
||||
import org.springframework.data.elasticsearch.annotations.SourceFilters;
|
||||
import org.springframework.data.elasticsearch.core.AbstractElasticsearchTemplate;
|
||||
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
|
||||
import org.springframework.data.elasticsearch.core.SearchHit;
|
||||
import org.springframework.data.elasticsearch.core.SearchHits;
|
||||
@ -76,6 +75,7 @@ import org.springframework.lang.Nullable;
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Rasmus Faber-Espensen
|
||||
* @author James Mudd
|
||||
* @author Haibo Liu
|
||||
*/
|
||||
@SpringIntegrationTest
|
||||
public abstract class CustomMethodRepositoryIntegrationTests {
|
||||
@ -1548,6 +1548,26 @@ public abstract class CustomMethodRepositoryIntegrationTests {
|
||||
assertThat(searchHit.getHighlightField("type")).hasSize(1).contains("<em>abc</em>");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnDifferentHighlightsOnAnnotatedStringQueryMethod() {
|
||||
List<SampleEntity> entities = createSampleEntities("abc xyz", 2);
|
||||
repository.saveAll(entities);
|
||||
|
||||
// when
|
||||
SearchHits<SampleEntity> highlightAbcHits = repository.queryByStringWithSeparateHighlight("abc", "abc");
|
||||
|
||||
assertThat(highlightAbcHits.getTotalHits()).isEqualTo(2);
|
||||
SearchHit<SampleEntity> highlightAbcHit = highlightAbcHits.getSearchHit(0);
|
||||
assertThat(highlightAbcHit.getHighlightField("type")).hasSize(1).contains("<em>abc</em> xyz");
|
||||
|
||||
// when
|
||||
SearchHits<SampleEntity> highlightXyzHits = repository.queryByStringWithSeparateHighlight("abc", "xyz");
|
||||
|
||||
assertThat(highlightXyzHits.getTotalHits()).isEqualTo(2);
|
||||
SearchHit<SampleEntity> highlightXyzHit = highlightXyzHits.getSearchHit(0);
|
||||
assertThat(highlightXyzHit.getHighlightField("type")).hasSize(1).contains("abc <em>xyz</em>");
|
||||
}
|
||||
|
||||
@Test // DATAES-734
|
||||
void shouldUseGeoSortParameter() {
|
||||
GeoPoint munich = new GeoPoint(48.137154, 11.5761247);
|
||||
@ -1920,6 +1940,41 @@ public abstract class CustomMethodRepositoryIntegrationTests {
|
||||
@Highlight(fields = { @HighlightField(name = "type") })
|
||||
SearchHits<SampleEntity> queryByString(String type);
|
||||
|
||||
@Query("""
|
||||
{
|
||||
"bool":{
|
||||
"must":[
|
||||
{
|
||||
"match":{
|
||||
"type":"?0"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
@Highlight(
|
||||
fields = {@HighlightField(name = "type")},
|
||||
parameters = @HighlightParameters(
|
||||
highlightQuery = @Query("""
|
||||
{
|
||||
"bool":{
|
||||
"must":[
|
||||
{
|
||||
"match":{
|
||||
"type":"?1"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
)
|
||||
)
|
||||
SearchHits<SampleEntity> queryByStringWithSeparateHighlight(String type, String highlight);
|
||||
|
||||
List<SearchHit<SampleEntity>> queryByMessage(String message);
|
||||
|
||||
Stream<SearchHit<SampleEntity>> readByMessage(String message);
|
||||
|
Loading…
x
Reference in New Issue
Block a user