mirror of
https://github.com/spring-projects/spring-data-elasticsearch.git
synced 2025-06-29 07:12:26 +00:00
Custom Order class with specific parameters for Elasticsearch.
Original Pull Request #1955 Closes #1911
This commit is contained in:
parent
7ae55b9e75
commit
b8f8a60fd7
@ -7,7 +7,8 @@ It is recommended to add those operations as custom implementation as described
|
||||
[[elasticsearc.misc.index.settings]]
|
||||
== Index settings
|
||||
|
||||
When creating Elasticsearch indices with Spring Data Elasticsearch different index settings can be defined by using the `@Setting` annotation. The following arguments are available:
|
||||
When creating Elasticsearch indices with Spring Data Elasticsearch different index settings can be defined by using the `@Setting` annotation.
|
||||
The following arguments are available:
|
||||
|
||||
* `useServerConfiguration` does not send any settings parameters, so the Elasticsearch server configuration determines them.
|
||||
* `settingPath` refers to a JSON file defining the settings that must be resolvable in the classpath
|
||||
@ -42,6 +43,7 @@ class Entity {
|
||||
// getter and setter...
|
||||
}
|
||||
----
|
||||
|
||||
<.> when defining sort fields, use the name of the Java property (_firstField_), not the name that might be defined for Elasticsearch (_first_field_)
|
||||
<.> `sortModes`, `sortOrders` and `sortMissingValues` are optional, but if they are set, the number of entries must match the number of `sortFields` elements
|
||||
====
|
||||
@ -49,13 +51,16 @@ class Entity {
|
||||
[[elasticsearch.misc.mappings]]
|
||||
== Index Mapping
|
||||
|
||||
When Spring Data Elasticsearch creates the index mapping with the `IndexOperations.createMapping()` methods, it uses the annotations described in <<elasticsearch.mapping.meta-model.annotations>>, especially the `@Field` annotation. In addition to that it is possible to add the `@Mapping` annotation to a class. This annotation has the following properties:
|
||||
When Spring Data Elasticsearch creates the index mapping with the `IndexOperations.createMapping()` methods, it uses the annotations described in <<elasticsearch.mapping.meta-model.annotations>>, especially the `@Field` annotation.
|
||||
In addition to that it is possible to add the `@Mapping` annotation to a class.
|
||||
This annotation has the following properties:
|
||||
|
||||
* `mappingPath` a classpath resource in JSON format; if this is not empty it is used as the mapping, no other mapping processing is done.
|
||||
* `enabled` when set to false, this flag is written to the mapping and no further processing is done.
|
||||
* `dateDetection` and `numericDetection` set the corresponding properties in the mapping when not set to `DEFAULT`.
|
||||
* `dynamicDateFormats` when this String array is not empty, it defines the date formats used for automatic date detection.
|
||||
* `runtimeFieldsPath` a classpath resource in JSON format containing the definition of runtime fields which is written to the index mappings, for example:
|
||||
|
||||
====
|
||||
[source,json]
|
||||
----
|
||||
@ -165,7 +170,10 @@ interface SampleEntityRepository extends Repository<SampleEntity, String> {
|
||||
[[elasticsearch.misc.sorts]]
|
||||
== Sort options
|
||||
|
||||
In addition to the default sort options described <<repositories.paging-and-sorting>> Spring Data Elasticsearch has a `GeoDistanceOrder` class which can be used to have the result of a search operation ordered by geographical distance.
|
||||
In addition to the default sort options described in <<repositories.paging-and-sorting>>, Spring Data Elasticsearch provides the class `org.springframework.data.elasticsearch.core.query.Order` which derives from `org.springframework.data.domain.Sort.Order`.
|
||||
It offers additional parameters that can be sent to Elasticsearch when specifying the sorting of the result (see https://www.elastic.co/guide/en/elasticsearch/reference/7.15/sort-search-results.html).
|
||||
|
||||
There also is the `org.springframework.data.elasticsearch.core.query.GeoDistanceOrder` class which can be used to have the result of a search operation ordered by geographical distance.
|
||||
|
||||
If the class to be retrieved has a `GeoPoint` property named _location_, the following `Sort` would sort the results by distance to the given point:
|
||||
|
||||
|
@ -1212,6 +1212,15 @@ class RequestFactory {
|
||||
private SortBuilder<?> getSortBuilder(Sort.Order order, @Nullable ElasticsearchPersistentEntity<?> entity) {
|
||||
SortOrder sortOrder = order.getDirection().isDescending() ? SortOrder.DESC : SortOrder.ASC;
|
||||
|
||||
Order.Mode mode = Order.DEFAULT_MODE;
|
||||
String unmappedType = null;
|
||||
|
||||
if (order instanceof Order) {
|
||||
Order o = (Order) order;
|
||||
mode = o.getMode();
|
||||
unmappedType = o.getUnmappedType();
|
||||
}
|
||||
|
||||
if (ScoreSortBuilder.NAME.equals(order.getProperty())) {
|
||||
return SortBuilders //
|
||||
.scoreSort() //
|
||||
@ -1229,14 +1238,23 @@ class RequestFactory {
|
||||
geoDistanceOrder.getGeoPoint().getLon());
|
||||
|
||||
sort.geoDistance(GeoDistance.fromString(geoDistanceOrder.getDistanceType().name()));
|
||||
sort.ignoreUnmapped(geoDistanceOrder.getIgnoreUnmapped());
|
||||
sort.sortMode(SortMode.fromString(geoDistanceOrder.getMode().name()));
|
||||
sort.sortMode(SortMode.fromString(mode.name()));
|
||||
sort.unit(DistanceUnit.fromString(geoDistanceOrder.getUnit()));
|
||||
|
||||
if (geoDistanceOrder.getIgnoreUnmapped() != GeoDistanceOrder.DEFAULT_IGNORE_UNMAPPED) {
|
||||
sort.ignoreUnmapped(geoDistanceOrder.getIgnoreUnmapped());
|
||||
}
|
||||
|
||||
return sort;
|
||||
} else {
|
||||
FieldSortBuilder sort = SortBuilders //
|
||||
.fieldSort(fieldName) //
|
||||
.order(sortOrder);
|
||||
.order(sortOrder) //
|
||||
.sortMode(SortMode.fromString(mode.name()));
|
||||
|
||||
if (unmappedType != null) {
|
||||
sort.unmappedType(unmappedType);
|
||||
}
|
||||
|
||||
if (order.getNullHandling() == Sort.NullHandling.NULLS_FIRST) {
|
||||
sort.missing("_first");
|
||||
|
@ -25,16 +25,14 @@ import org.springframework.data.elasticsearch.core.geo.GeoPoint;
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 4.0
|
||||
*/
|
||||
public class GeoDistanceOrder extends Sort.Order {
|
||||
public class GeoDistanceOrder extends Order {
|
||||
|
||||
private static final DistanceType DEFAULT_DISTANCE_TYPE = DistanceType.arc;
|
||||
private static final Mode DEFAULT_MODE = Mode.min;
|
||||
private static final String DEFAULT_UNIT = "m";
|
||||
private static final Boolean DEFAULT_IGNORE_UNMAPPED = false;
|
||||
public static final DistanceType DEFAULT_DISTANCE_TYPE = DistanceType.arc;
|
||||
public static final String DEFAULT_UNIT = "m";
|
||||
public static final Boolean DEFAULT_IGNORE_UNMAPPED = false;
|
||||
|
||||
private final GeoPoint geoPoint;
|
||||
private final DistanceType distanceType;
|
||||
private final Mode mode;
|
||||
private final String unit;
|
||||
private final Boolean ignoreUnmapped;
|
||||
|
||||
@ -45,10 +43,9 @@ public class GeoDistanceOrder extends Sort.Order {
|
||||
|
||||
private GeoDistanceOrder(String property, GeoPoint geoPoint, Sort.Direction direction, DistanceType distanceType,
|
||||
Mode mode, String unit, Boolean ignoreUnmapped) {
|
||||
super(direction, property);
|
||||
super(direction, property, mode);
|
||||
this.geoPoint = geoPoint;
|
||||
this.distanceType = distanceType;
|
||||
this.mode = mode;
|
||||
this.unit = unit;
|
||||
this.ignoreUnmapped = ignoreUnmapped;
|
||||
}
|
||||
@ -119,8 +116,4 @@ public class GeoDistanceOrder extends Sort.Order {
|
||||
public enum DistanceType {
|
||||
arc, plane
|
||||
}
|
||||
|
||||
public enum Mode {
|
||||
min, max, median, avg
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2021 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.core.query;
|
||||
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Extends the {@link Sort.Order} with properties that can be set on Elasticsearch order options.
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 4.3
|
||||
*/
|
||||
public class Order extends Sort.Order {
|
||||
|
||||
public static final Mode DEFAULT_MODE = Mode.min;
|
||||
public static final Sort.NullHandling DEFAULT_NULL_HANDLING = Sort.NullHandling.NATIVE;
|
||||
|
||||
protected final Mode mode;
|
||||
@Nullable protected final String unmappedType;
|
||||
|
||||
public Order(Sort.Direction direction, String property) {
|
||||
this(direction, property, DEFAULT_MODE, null);
|
||||
}
|
||||
|
||||
public Order(Sort.Direction direction, String property, Mode mode) {
|
||||
this(direction, property, DEFAULT_NULL_HANDLING, mode, null);
|
||||
}
|
||||
|
||||
public Order(Sort.Direction direction, String property, @Nullable String unmappedType) {
|
||||
this(direction, property, DEFAULT_NULL_HANDLING, DEFAULT_MODE, unmappedType);
|
||||
}
|
||||
|
||||
public Order(Sort.Direction direction, String property, Mode mode, @Nullable String unmappedType) {
|
||||
this(direction, property, DEFAULT_NULL_HANDLING, mode, unmappedType);
|
||||
}
|
||||
|
||||
public Order(Sort.Direction direction, String property, Sort.NullHandling nullHandlingHint) {
|
||||
this(direction, property, nullHandlingHint, DEFAULT_MODE, null);
|
||||
}
|
||||
|
||||
public Order(Sort.Direction direction, String property, Sort.NullHandling nullHandlingHint, Mode mode) {
|
||||
this(direction, property, nullHandlingHint, mode, null);
|
||||
}
|
||||
|
||||
public Order(Sort.Direction direction, String property, Sort.NullHandling nullHandlingHint,
|
||||
@Nullable String unmappedType) {
|
||||
this(direction, property, nullHandlingHint, DEFAULT_MODE, unmappedType);
|
||||
}
|
||||
|
||||
public Order(Sort.Direction direction, String property, Sort.NullHandling nullHandlingHint, Mode mode,
|
||||
@Nullable String unmappedType) {
|
||||
super(direction, property, nullHandlingHint);
|
||||
this.mode = mode;
|
||||
this.unmappedType = unmappedType;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getUnmappedType() {
|
||||
return unmappedType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Sort.Order with(Sort.Direction direction) {
|
||||
return new Order(direction, getProperty(), getNullHandling(), mode, unmappedType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Sort.Order withProperty(String property) {
|
||||
return new Order(getDirection(), property, getNullHandling(), mode, unmappedType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Sort.Order with(Sort.NullHandling nullHandling) {
|
||||
return new Order(getDirection(), getProperty(), nullHandling, getMode(), unmappedType);
|
||||
}
|
||||
|
||||
public Order withUnmappedType(@Nullable String unmappedType) {
|
||||
return new Order(getDirection(), getProperty(), getNullHandling(), getMode(), unmappedType);
|
||||
}
|
||||
|
||||
public Order with(Mode mode) {
|
||||
return new Order(getDirection(), getProperty(), getNullHandling(), mode, unmappedType);
|
||||
}
|
||||
|
||||
public Mode getMode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
public enum Mode {
|
||||
min, max, median, avg
|
||||
}
|
||||
|
||||
}
|
@ -686,9 +686,11 @@ public abstract class ElasticsearchTemplateTests {
|
||||
|
||||
operations.bulkIndex(indexQueries, IndexCoordinates.of(indexNameProvider.indexName()));
|
||||
|
||||
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery())
|
||||
.withSort(new FieldSortBuilder("rate").order(SortOrder.ASC))
|
||||
.withSort(new FieldSortBuilder("message").order(SortOrder.ASC)).build();
|
||||
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()) //
|
||||
.withSorts( //
|
||||
new FieldSortBuilder("rate").order(SortOrder.ASC), //
|
||||
new FieldSortBuilder("message").order(SortOrder.ASC)) //
|
||||
.build();
|
||||
|
||||
// when
|
||||
SearchHits<SampleEntity> searchHits = operations.search(searchQuery, SampleEntity.class,
|
||||
@ -725,8 +727,8 @@ public abstract class ElasticsearchTemplateTests {
|
||||
|
||||
operations.bulkIndex(indexQueries, IndexCoordinates.of(indexNameProvider.indexName()));
|
||||
|
||||
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery())
|
||||
.withPageable(PageRequest.of(0, 10, Sort.by(Sort.Order.asc("message").nullsFirst()))).build();
|
||||
Query searchQuery = operations.matchAllQuery();
|
||||
searchQuery.setPageable(PageRequest.of(0, 10, Sort.by(Sort.Order.asc("message").nullsFirst())));
|
||||
|
||||
// when
|
||||
SearchHits<SampleEntity> searchHits = operations.search(searchQuery, SampleEntity.class,
|
||||
@ -763,8 +765,8 @@ public abstract class ElasticsearchTemplateTests {
|
||||
|
||||
operations.bulkIndex(indexQueries, IndexCoordinates.of(indexNameProvider.indexName()));
|
||||
|
||||
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery())
|
||||
.withPageable(PageRequest.of(0, 10, Sort.by(Sort.Order.asc("message").nullsLast()))).build();
|
||||
Query searchQuery = operations.matchAllQuery();
|
||||
searchQuery.setPageable(PageRequest.of(0, 10, Sort.by(Sort.Order.asc("message").nullsLast())));
|
||||
|
||||
// when
|
||||
SearchHits<SampleEntity> searchHits = operations.search(searchQuery, SampleEntity.class,
|
||||
@ -1139,8 +1141,8 @@ public abstract class ElasticsearchTemplateTests {
|
||||
|
||||
// then
|
||||
|
||||
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery())
|
||||
.withPageable(PageRequest.of(0, 10)).build();
|
||||
Query searchQuery = operations.matchAllQuery();
|
||||
searchQuery.setPageable(PageRequest.of(0, 10));
|
||||
|
||||
SearchScrollHits<SampleEntity> scroll = ((AbstractElasticsearchTemplate) operations).searchScrollStart(1000,
|
||||
searchQuery, SampleEntity.class, IndexCoordinates.of(indexNameProvider.indexName()));
|
||||
@ -3591,6 +3593,29 @@ public abstract class ElasticsearchTemplateTests {
|
||||
operations.index(query, IndexCoordinates.of(indexNameProvider.indexName()));
|
||||
}
|
||||
|
||||
@Test // #1945
|
||||
@DisplayName("should error on sort with unmapped field and default settings")
|
||||
void shouldErrorOnSortWithUnmappedFieldAndDefaultSettings() {
|
||||
|
||||
Sort.Order order = new Sort.Order(Sort.Direction.ASC, "unmappedField");
|
||||
Query query = operations.matchAllQuery().addSort(Sort.by(order));
|
||||
|
||||
assertThatThrownBy(() -> {
|
||||
operations.search(query, SampleEntity.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test // #1945
|
||||
@DisplayName("should not error on sort with unmapped field and unmapped_type settings")
|
||||
void shouldNotErrorOnSortWithUnmappedFieldAndUnmappedTypeSettings() {
|
||||
|
||||
org.springframework.data.elasticsearch.core.query.Order order = new org.springframework.data.elasticsearch.core.query.Order(
|
||||
Sort.Direction.ASC, "unmappedField").withUnmappedType("long");
|
||||
Query query = operations.matchAllQuery().addSort(Sort.by(order));
|
||||
|
||||
operations.search(query, SampleEntity.class);
|
||||
}
|
||||
|
||||
// region entities
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
@Setting(shards = 1, replicas = 0, refreshInterval = "-1")
|
||||
|
Loading…
x
Reference in New Issue
Block a user