mirror of
https://github.com/spring-projects/spring-data-elasticsearch.git
synced 2025-07-03 17:22:10 +00:00
parent
44a79093ce
commit
df5fd0b97c
@ -53,7 +53,7 @@ interface BookRepository extends Repository<Book, String> {
|
|||||||
@HighlightField(name = "name"),
|
@HighlightField(name = "name"),
|
||||||
@HighlightField(name = "summary")
|
@HighlightField(name = "summary")
|
||||||
})
|
})
|
||||||
List<SearchHit<Book>> findByNameOrSummary(String text, String summary);
|
SearchHits<Book> findByNameOrSummary(String text, String summary);
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
====
|
====
|
||||||
@ -62,6 +62,30 @@ It is possible to define multiple fields to be highlighted like above, and both
|
|||||||
|
|
||||||
In the search results the highlight data can be retrieved from the `SearchHit` class.
|
In the search results the highlight data can be retrieved from the `SearchHit` class.
|
||||||
|
|
||||||
|
=== @SourceFilters
|
||||||
|
|
||||||
|
Sometimes the user does not need to have all the properties of an entity returned from a search but only a subset.
|
||||||
|
Elasticsearch provides source filtering to reduce the amount of data that is transferred across the network to the
|
||||||
|
application.
|
||||||
|
|
||||||
|
When working with `Query` implementations and the `ElasticsearchOperations` this is easily possible by setting a
|
||||||
|
source filter on the query.
|
||||||
|
|
||||||
|
When using repository methods there is the `@SourceFilters` annotation:
|
||||||
|
|
||||||
|
====
|
||||||
|
[source,java]
|
||||||
|
----
|
||||||
|
interface BookRepository extends Repository<Book, String> {
|
||||||
|
|
||||||
|
@SourceFilters(includes = "name")
|
||||||
|
SearchHits<Book> findByName(String text);
|
||||||
|
}
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
|
In this example, all the properties of the returned `Book` objects would be `null` except the name.
|
||||||
|
|
||||||
[[elasticsearch.annotation]]
|
[[elasticsearch.annotation]]
|
||||||
== Annotation based configuration
|
== Annotation based configuration
|
||||||
|
|
||||||
|
@ -16,11 +16,11 @@
|
|||||||
package org.springframework.data.elasticsearch.annotations;
|
package org.springframework.data.elasticsearch.annotations;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Values based on reference doc - https://www.elastic.co/guide/reference/mapping/date-format/. The patterns are taken
|
* Values based on <a href="https://www.elastic.co/guide/reference/mapping/date-format/">Elasticsearch reference
|
||||||
* from this documentation and slightly adapted so that a Java {@link java.time.format.DateTimeFormatter} produces the
|
* documentation</a>. The patterns are taken from this documentation and slightly adapted so that a Java
|
||||||
* same values as the Elasticsearch formatter. Use <code>format = {}</code> to disable built-in date * formats in
|
* {@link java.time.format.DateTimeFormatter} produces the same values as the Elasticsearch formatter. Use
|
||||||
* the @Field annotation. If you want to use only a custom date format pattern, you must set the <code>format</code> *
|
* <code>format = {}</code> to disable built-in date formats in the {@link Field} annotation. If you want to use only a
|
||||||
* property to empty <code>{}</code>.
|
* custom date format pattern, you must set the <code>format</code> property to empty <code>{}</code>.
|
||||||
*
|
*
|
||||||
* @author Jakub Vavrik
|
* @author Jakub Vavrik
|
||||||
* @author Tim te Beek
|
* @author Tim te Beek
|
||||||
@ -49,7 +49,7 @@ public enum DateFormat {
|
|||||||
date_hour_minute_second_millis("uuuu-MM-dd'T'HH:mm:ss.SSS"), //
|
date_hour_minute_second_millis("uuuu-MM-dd'T'HH:mm:ss.SSS"), //
|
||||||
date_optional_time("uuuu-MM-dd['T'HH:mm:ss.SSSXXX]"), //
|
date_optional_time("uuuu-MM-dd['T'HH:mm:ss.SSSXXX]"), //
|
||||||
date_time("uuuu-MM-dd'T'HH:mm:ss.SSSXXX"), //
|
date_time("uuuu-MM-dd'T'HH:mm:ss.SSSXXX"), //
|
||||||
date_time_no_millis("uuuu-MM-dd'T'HH:mm:ssVV"), // here Elasticsearch uses the zone-id in it's implementation
|
date_time_no_millis("uuuu-MM-dd'T'HH:mm:ssVV"), // here Elasticsearch uses the zone-id in its implementation
|
||||||
epoch_millis("epoch_millis"), //
|
epoch_millis("epoch_millis"), //
|
||||||
epoch_second("epoch_second"), //
|
epoch_second("epoch_second"), //
|
||||||
hour("HH"), //
|
hour("HH"), //
|
||||||
|
@ -53,9 +53,8 @@ public @interface Field {
|
|||||||
String value() default "";
|
String value() default "";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The <em>name</em> to be used to store the field inside the document.
|
* The <em>name</em> to be used to store the field inside the document. If not set, the name of the annotated property
|
||||||
* <p>
|
* is used.
|
||||||
* √5 If not set, the name of the annotated property is used.
|
|
||||||
*
|
*
|
||||||
* @since 3.2
|
* @since 3.2
|
||||||
*/
|
*/
|
||||||
|
@ -1031,6 +1031,7 @@ class RequestConverter {
|
|||||||
Assert.notNull(query, "query must not be null");
|
Assert.notNull(query, "query must not be null");
|
||||||
Assert.notNull(indexCoordinates, "indexCoordinates must not be null");
|
Assert.notNull(indexCoordinates, "indexCoordinates must not be null");
|
||||||
|
|
||||||
|
elasticsearchConverter.updateQuery(query, clazz);
|
||||||
SearchRequest.Builder builder = new SearchRequest.Builder();
|
SearchRequest.Builder builder = new SearchRequest.Builder();
|
||||||
prepareSearchRequest(query, clazz, indexCoordinates, builder, forCount, useScroll);
|
prepareSearchRequest(query, clazz, indexCoordinates, builder, forCount, useScroll);
|
||||||
|
|
||||||
|
@ -15,19 +15,29 @@
|
|||||||
*/
|
*/
|
||||||
package org.springframework.data.elasticsearch.core.query;
|
package org.springframework.data.elasticsearch.core.query;
|
||||||
|
|
||||||
|
import org.springframework.data.elasticsearch.annotations.Field;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SourceFilter for providing includes and excludes.
|
* SourceFilter for providing includes and excludes. Using these helps in reducing the amount of data that is returned
|
||||||
|
* from Elasticsearch especially when the stored docuements are large and only some fields from these documents are
|
||||||
|
* needed. If the SourceFilter includes the name of a property that has a different name mapped in Elasticsearch (see
|
||||||
|
* {@link Field#name()} this will automatically be mapped.
|
||||||
*
|
*
|
||||||
* @author Jon Tsiros
|
* @author Jon Tsiros
|
||||||
* @author Peter-Josef Meisch
|
* @author Peter-Josef Meisch
|
||||||
*/
|
*/
|
||||||
public interface SourceFilter {
|
public interface SourceFilter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the name of the fields to include in a response.
|
||||||
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
String[] getIncludes();
|
String[] getIncludes();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the names of the fields to exclude from a response.
|
||||||
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
String[] getExcludes();
|
String[] getExcludes();
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,6 @@ public abstract class AbstractElasticsearchRepositoryQuery implements Repository
|
|||||||
|
|
||||||
protected void prepareQuery(Query query, Class<?> clazz, ParameterAccessor parameterAccessor) {
|
protected void prepareQuery(Query query, Class<?> clazz, ParameterAccessor parameterAccessor) {
|
||||||
|
|
||||||
elasticsearchConverter.updateQuery(query, clazz);
|
|
||||||
|
|
||||||
if (queryMethod.hasAnnotatedHighlight()) {
|
if (queryMethod.hasAnnotatedHighlight()) {
|
||||||
query.setHighlightQuery(queryMethod.getAnnotatedHighlightQuery());
|
query.setHighlightQuery(queryMethod.getAnnotatedHighlightQuery());
|
||||||
@ -68,5 +67,7 @@ public abstract class AbstractElasticsearchRepositoryQuery implements Repository
|
|||||||
if (sourceFilter != null) {
|
if (sourceFilter != null) {
|
||||||
query.addSourceFilter(sourceFilter);
|
query.addSourceFilter(sourceFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
elasticsearchConverter.updateQuery(query, clazz);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -131,6 +131,9 @@ public class ElasticsearchQueryMethod extends QueryMethod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private HighlightQuery createAnnotatedHighlightQuery() {
|
private HighlightQuery createAnnotatedHighlightQuery() {
|
||||||
|
|
||||||
|
Assert.notNull(highlightAnnotation, "highlightAnnotation must not be null");
|
||||||
|
|
||||||
return new HighlightQuery(
|
return new HighlightQuery(
|
||||||
org.springframework.data.elasticsearch.core.query.highlight.Highlight.of(highlightAnnotation),
|
org.springframework.data.elasticsearch.core.query.highlight.Highlight.of(highlightAnnotation),
|
||||||
getDomainClass());
|
getDomainClass());
|
||||||
@ -140,6 +143,7 @@ public class ElasticsearchQueryMethod extends QueryMethod {
|
|||||||
* @return the {@link ElasticsearchEntityMetadata} for the query methods {@link #getReturnedObjectType() return type}.
|
* @return the {@link ElasticsearchEntityMetadata} for the query methods {@link #getReturnedObjectType() return type}.
|
||||||
* @since 3.2
|
* @since 3.2
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
public ElasticsearchEntityMetadata<?> getEntityInformation() {
|
public ElasticsearchEntityMetadata<?> getEntityInformation() {
|
||||||
|
|
||||||
@ -260,23 +264,6 @@ public class ElasticsearchQueryMethod extends QueryMethod {
|
|||||||
return queryAnnotation != null && queryAnnotation.count();
|
return queryAnnotation != null && queryAnnotation.count();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return {@literal true} if the method is annotated with {@link SourceFilters}.
|
|
||||||
* @since 5.0
|
|
||||||
*/
|
|
||||||
public boolean hasSourceFilters() {
|
|
||||||
return sourceFilters != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the {@link SourceFilters} annotation for this method.
|
|
||||||
* @since 5.0
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public SourceFilters getSourceFilters() {
|
|
||||||
return sourceFilters;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uses the sourceFilters property to create a {@link SourceFilter} to be added to a
|
* Uses the sourceFilters property to create a {@link SourceFilter} to be added to a
|
||||||
* {@link org.springframework.data.elasticsearch.core.query.Query}
|
* {@link org.springframework.data.elasticsearch.core.query.Query}
|
||||||
@ -295,29 +282,25 @@ public class ElasticsearchQueryMethod extends QueryMethod {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
ElasticsearchPersistentEntity<?> persistentEntity = converter.getMappingContext()
|
|
||||||
.getPersistentEntity(getEntityInformation().getJavaType());
|
|
||||||
|
|
||||||
StringQueryUtil stringQueryUtil = new StringQueryUtil(converter.getConversionService());
|
StringQueryUtil stringQueryUtil = new StringQueryUtil(converter.getConversionService());
|
||||||
FetchSourceFilterBuilder fetchSourceFilterBuilder = new FetchSourceFilterBuilder();
|
FetchSourceFilterBuilder fetchSourceFilterBuilder = new FetchSourceFilterBuilder();
|
||||||
|
|
||||||
if (sourceFilters.includes().length > 0) {
|
if (sourceFilters.includes().length > 0) {
|
||||||
fetchSourceFilterBuilder
|
fetchSourceFilterBuilder
|
||||||
.withIncludes(mapParameters(sourceFilters.includes(), parameterAccessor, stringQueryUtil, persistentEntity));
|
.withIncludes(mapParameters(sourceFilters.includes(), parameterAccessor, stringQueryUtil));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sourceFilters.excludes().length > 0) {
|
if (sourceFilters.excludes().length > 0) {
|
||||||
fetchSourceFilterBuilder
|
fetchSourceFilterBuilder
|
||||||
.withExcludes(mapParameters(sourceFilters.excludes(), parameterAccessor, stringQueryUtil, persistentEntity));
|
.withExcludes(mapParameters(sourceFilters.excludes(), parameterAccessor, stringQueryUtil));
|
||||||
}
|
}
|
||||||
|
|
||||||
return fetchSourceFilterBuilder.build();
|
return fetchSourceFilterBuilder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String[] mapParameters(String[] source, ParameterAccessor parameterAccessor, StringQueryUtil stringQueryUtil,
|
private String[] mapParameters(String[] source, ParameterAccessor parameterAccessor, StringQueryUtil stringQueryUtil) {
|
||||||
@Nullable ElasticsearchPersistentEntity<?> persistentEntity) {
|
|
||||||
|
|
||||||
List<String> unmappedFieldNames = new ArrayList<>();
|
List<String> fieldNames = new ArrayList<>();
|
||||||
|
|
||||||
for (String s : source) {
|
for (String s : source) {
|
||||||
|
|
||||||
@ -325,21 +308,17 @@ public class ElasticsearchQueryMethod extends QueryMethod {
|
|||||||
String fieldName = stringQueryUtil.replacePlaceholders(s, parameterAccessor);
|
String fieldName = stringQueryUtil.replacePlaceholders(s, parameterAccessor);
|
||||||
// this could be "[\"foo\",\"bar\"]", must be split
|
// this could be "[\"foo\",\"bar\"]", must be split
|
||||||
if (fieldName.startsWith("[") && fieldName.endsWith("]")) {
|
if (fieldName.startsWith("[") && fieldName.endsWith("]")) {
|
||||||
unmappedFieldNames.addAll( //
|
//noinspection RegExpRedundantEscape
|
||||||
|
fieldNames.addAll( //
|
||||||
Arrays.asList(fieldName.substring(1, fieldName.length() - 2) //
|
Arrays.asList(fieldName.substring(1, fieldName.length() - 2) //
|
||||||
.replaceAll("\\\"", "") //
|
.replaceAll("\\\"", "") //
|
||||||
.split(","))); //
|
.split(","))); //
|
||||||
} else {
|
} else {
|
||||||
unmappedFieldNames.add(fieldName);
|
fieldNames.add(fieldName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return unmappedFieldNames.stream().map(fieldName -> {
|
return fieldNames.toArray(new String[0]);
|
||||||
ElasticsearchPersistentProperty property = persistentEntity != null
|
|
||||||
? persistentEntity.getPersistentProperty(fieldName)
|
|
||||||
: null;
|
|
||||||
return property != null ? property.getFieldName() : fieldName;
|
|
||||||
}).toArray(String[]::new);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user