mirror of
https://github.com/spring-projects/spring-data-elasticsearch.git
synced 2025-06-22 20:12:11 +00:00
Fix mapping of property names in sort parameters.
Original Pull Request #3074 Closes #3072 Signed-off-by: Peter-Josef Meisch <pj.meisch@sothawo.com>
This commit is contained in:
parent
35e7b45f1a
commit
42383624ea
@ -38,6 +38,7 @@ import org.springframework.core.env.Environment;
|
|||||||
import org.springframework.core.env.EnvironmentCapable;
|
import org.springframework.core.env.EnvironmentCapable;
|
||||||
import org.springframework.core.env.StandardEnvironment;
|
import org.springframework.core.env.StandardEnvironment;
|
||||||
import org.springframework.data.convert.CustomConversions;
|
import org.springframework.data.convert.CustomConversions;
|
||||||
|
import org.springframework.data.domain.Sort;
|
||||||
import org.springframework.data.elasticsearch.annotations.FieldType;
|
import org.springframework.data.elasticsearch.annotations.FieldType;
|
||||||
import org.springframework.data.elasticsearch.annotations.ScriptedField;
|
import org.springframework.data.elasticsearch.annotations.ScriptedField;
|
||||||
import org.springframework.data.elasticsearch.core.document.Document;
|
import org.springframework.data.elasticsearch.core.document.Document;
|
||||||
@ -50,6 +51,7 @@ import org.springframework.data.elasticsearch.core.query.Criteria;
|
|||||||
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
|
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
|
||||||
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
|
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
|
||||||
import org.springframework.data.elasticsearch.core.query.Field;
|
import org.springframework.data.elasticsearch.core.query.Field;
|
||||||
|
import org.springframework.data.elasticsearch.core.query.Order;
|
||||||
import org.springframework.data.elasticsearch.core.query.Query;
|
import org.springframework.data.elasticsearch.core.query.Query;
|
||||||
import org.springframework.data.elasticsearch.core.query.SeqNoPrimaryTerm;
|
import org.springframework.data.elasticsearch.core.query.SeqNoPrimaryTerm;
|
||||||
import org.springframework.data.elasticsearch.core.query.SourceFilter;
|
import org.springframework.data.elasticsearch.core.query.SourceFilter;
|
||||||
@ -1231,7 +1233,7 @@ public class MappingElasticsearchConverter
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
updatePropertiesInFieldsAndSourceFilter(query, domainClass);
|
updatePropertiesInFieldsSortAndSourceFilter(query, domainClass);
|
||||||
|
|
||||||
if (query instanceof CriteriaQuery criteriaQuery) {
|
if (query instanceof CriteriaQuery criteriaQuery) {
|
||||||
updatePropertiesInCriteriaQuery(criteriaQuery, domainClass);
|
updatePropertiesInCriteriaQuery(criteriaQuery, domainClass);
|
||||||
@ -1242,7 +1244,14 @@ public class MappingElasticsearchConverter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updatePropertiesInFieldsAndSourceFilter(Query query, Class<?> domainClass) {
|
/**
|
||||||
|
* replaces the names of fields in the query, the sort or soucre filters with the field names used in Elasticsearch
|
||||||
|
* when they are defined on the ElasticsearchProperties
|
||||||
|
*
|
||||||
|
* @param query the query to process
|
||||||
|
* @param domainClass the domain class (persistent entity)
|
||||||
|
*/
|
||||||
|
private void updatePropertiesInFieldsSortAndSourceFilter(Query query, Class<?> domainClass) {
|
||||||
|
|
||||||
ElasticsearchPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(domainClass);
|
ElasticsearchPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(domainClass);
|
||||||
|
|
||||||
@ -1250,12 +1259,12 @@ public class MappingElasticsearchConverter
|
|||||||
List<String> fields = query.getFields();
|
List<String> fields = query.getFields();
|
||||||
|
|
||||||
if (!fields.isEmpty()) {
|
if (!fields.isEmpty()) {
|
||||||
query.setFields(updateFieldNames(fields, persistentEntity));
|
query.setFields(propertyToFieldNames(fields, persistentEntity));
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> storedFields = query.getStoredFields();
|
List<String> storedFields = query.getStoredFields();
|
||||||
if (!CollectionUtils.isEmpty(storedFields)) {
|
if (!CollectionUtils.isEmpty(storedFields)) {
|
||||||
query.setStoredFields(updateFieldNames(storedFields, persistentEntity));
|
query.setStoredFields(propertyToFieldNames(storedFields, persistentEntity));
|
||||||
}
|
}
|
||||||
|
|
||||||
SourceFilter sourceFilter = query.getSourceFilter();
|
SourceFilter sourceFilter = query.getSourceFilter();
|
||||||
@ -1266,37 +1275,60 @@ public class MappingElasticsearchConverter
|
|||||||
String[] excludes = null;
|
String[] excludes = null;
|
||||||
|
|
||||||
if (sourceFilter.getIncludes() != null) {
|
if (sourceFilter.getIncludes() != null) {
|
||||||
includes = updateFieldNames(Arrays.asList(sourceFilter.getIncludes()), persistentEntity)
|
includes = propertyToFieldNames(Arrays.asList(sourceFilter.getIncludes()), persistentEntity)
|
||||||
.toArray(new String[] {});
|
.toArray(new String[] {});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sourceFilter.getExcludes() != null) {
|
if (sourceFilter.getExcludes() != null) {
|
||||||
excludes = updateFieldNames(Arrays.asList(sourceFilter.getExcludes()), persistentEntity)
|
excludes = propertyToFieldNames(Arrays.asList(sourceFilter.getExcludes()), persistentEntity)
|
||||||
.toArray(new String[] {});
|
.toArray(new String[] {});
|
||||||
}
|
}
|
||||||
|
|
||||||
query.addSourceFilter(new FetchSourceFilter(sourceFilter.fetchSource(), includes, excludes));
|
query.addSourceFilter(new FetchSourceFilter(sourceFilter.fetchSource(), includes, excludes));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (query.getSort() != null) {
|
||||||
|
var sort = query.getSort();
|
||||||
|
// stream the orders and map them to a new order with the changed names,
|
||||||
|
// then replace the existing sort with a new sort containing the new orders.
|
||||||
|
var newOrders = sort.stream().map(order -> {
|
||||||
|
var fieldNames = updateFieldNames(order.getProperty(), persistentEntity);
|
||||||
|
|
||||||
|
if (order instanceof Order springDataElasticsearchOrder) {
|
||||||
|
return springDataElasticsearchOrder.withProperty(fieldNames);
|
||||||
|
} else {
|
||||||
|
return new Sort.Order(order.getDirection(),
|
||||||
|
fieldNames,
|
||||||
|
order.isIgnoreCase(),
|
||||||
|
order.getNullHandling());
|
||||||
|
}
|
||||||
|
}).toList();
|
||||||
|
|
||||||
|
if (query instanceof BaseQuery baseQuery) {
|
||||||
|
baseQuery.setSort(Sort.by(newOrders));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* relaces the fieldName with the property name of a property of the persistentEntity with the corresponding
|
* replaces property name of a property of the persistentEntity with the corresponding fieldname. If no such property
|
||||||
* fieldname. If no such property exists, the original fieldName is kept.
|
* exists, the original fieldName is kept.
|
||||||
*
|
*
|
||||||
* @param fieldNames list of fieldnames
|
* @param propertyNames list of fieldnames
|
||||||
* @param persistentEntity the persistent entity to check
|
* @param persistentEntity the persistent entity to check
|
||||||
* @return an updated list of field names
|
* @return an updated list of field names
|
||||||
*/
|
*/
|
||||||
private List<String> updateFieldNames(List<String> fieldNames, ElasticsearchPersistentEntity<?> persistentEntity) {
|
private List<String> propertyToFieldNames(List<String> propertyNames,
|
||||||
return fieldNames.stream().map(fieldName -> updateFieldName(persistentEntity, fieldName))
|
ElasticsearchPersistentEntity<?> persistentEntity) {
|
||||||
|
return propertyNames.stream().map(propertyName -> propertyToFieldName(persistentEntity, propertyName))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private String updateFieldName(ElasticsearchPersistentEntity<?> persistentEntity, String fieldName) {
|
private String propertyToFieldName(ElasticsearchPersistentEntity<?> persistentEntity, String propertyName) {
|
||||||
ElasticsearchPersistentProperty persistentProperty = persistentEntity.getPersistentProperty(fieldName);
|
ElasticsearchPersistentProperty persistentProperty = persistentEntity.getPersistentProperty(propertyName);
|
||||||
return persistentProperty != null ? persistentProperty.getFieldName() : fieldName;
|
return persistentProperty != null ? persistentProperty.getFieldName() : propertyName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updatePropertiesInCriteriaQuery(CriteriaQuery criteriaQuery, Class<?> domainClass) {
|
private void updatePropertiesInCriteriaQuery(CriteriaQuery criteriaQuery, Class<?> domainClass) {
|
||||||
@ -1410,7 +1442,7 @@ public class MappingElasticsearchConverter
|
|||||||
|
|
||||||
if (properties.length > 0) {
|
if (properties.length > 0) {
|
||||||
var propertyName = properties[0];
|
var propertyName = properties[0];
|
||||||
var fieldName = updateFieldName(persistentEntity, propertyName);
|
var fieldName = propertyToFieldName(persistentEntity, propertyName);
|
||||||
|
|
||||||
if (properties.length > 1) {
|
if (properties.length > 1) {
|
||||||
var persistentProperty = persistentEntity.getPersistentProperty(propertyName);
|
var persistentProperty = persistentEntity.getPersistentProperty(propertyName);
|
||||||
@ -1431,7 +1463,6 @@ public class MappingElasticsearchConverter
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
@SuppressWarnings("ClassCanBeRecord")
|
@SuppressWarnings("ClassCanBeRecord")
|
||||||
|
@ -23,6 +23,7 @@ import java.util.Collection;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.annotation.Id;
|
import org.springframework.data.annotation.Id;
|
||||||
@ -35,7 +36,6 @@ import org.springframework.data.elasticsearch.repository.query.ElasticsearchQuer
|
|||||||
import org.springframework.data.elasticsearch.repository.query.RepositoryPartQuery;
|
import org.springframework.data.elasticsearch.repository.query.RepositoryPartQuery;
|
||||||
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||||
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
|
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
|
||||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
|
||||||
import org.springframework.data.repository.query.ValueExpressionDelegate;
|
import org.springframework.data.repository.query.ValueExpressionDelegate;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
|
|
||||||
@ -640,6 +640,45 @@ public abstract class RepositoryPartQueryIntegrationTests {
|
|||||||
assertEquals(expected, query, false);
|
assertEquals(expected, query, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test // #3072
|
||||||
|
@DisplayName("should build sort object with correct field names")
|
||||||
|
void shouldBuildSortObjectWithCorrectFieldNames() throws NoSuchMethodException, JSONException {
|
||||||
|
|
||||||
|
String methodName = "findByNameOrderBySortAuthor_SortName";
|
||||||
|
Class<?>[] parameterClasses = new Class[] { String.class };
|
||||||
|
Object[] parameters = new Object[] { BOOK_TITLE };
|
||||||
|
|
||||||
|
String query = getQueryString(methodName, parameterClasses, parameters);
|
||||||
|
|
||||||
|
String expected = """
|
||||||
|
|
||||||
|
{
|
||||||
|
"query": {
|
||||||
|
"bool": {
|
||||||
|
"must": [
|
||||||
|
{
|
||||||
|
"query_string": {
|
||||||
|
"query": "Title",
|
||||||
|
"fields": [
|
||||||
|
"name"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sort": [
|
||||||
|
{
|
||||||
|
"sort_author.sort_name": {
|
||||||
|
"order": "asc"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}""";
|
||||||
|
|
||||||
|
assertEquals(expected, query, false);
|
||||||
|
}
|
||||||
|
|
||||||
private String getQueryString(String methodName, Class<?>[] parameterClasses, Object[] parameters)
|
private String getQueryString(String methodName, Class<?>[] parameterClasses, Object[] parameters)
|
||||||
throws NoSuchMethodException {
|
throws NoSuchMethodException {
|
||||||
|
|
||||||
@ -727,6 +766,8 @@ public abstract class RepositoryPartQueryIntegrationTests {
|
|||||||
|
|
||||||
List<Book> findByAvailableTrueOrderByNameDesc();
|
List<Book> findByAvailableTrueOrderByNameDesc();
|
||||||
|
|
||||||
|
List<Book> findByNameOrderBySortAuthor_SortName(String name);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Book {
|
public static class Book {
|
||||||
@ -736,6 +777,10 @@ public abstract class RepositoryPartQueryIntegrationTests {
|
|||||||
@Nullable private Integer price;
|
@Nullable private Integer price;
|
||||||
@Field(type = FieldType.Boolean) private boolean available;
|
@Field(type = FieldType.Boolean) private boolean available;
|
||||||
|
|
||||||
|
// this is needed for the #3072 test
|
||||||
|
@Nullable
|
||||||
|
@Field(name = "sort_author", type = FieldType.Object) private Author sortAuthor;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return id;
|
return id;
|
||||||
@ -767,8 +812,32 @@ public abstract class RepositoryPartQueryIntegrationTests {
|
|||||||
return available;
|
return available;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Author getSortAuthor() {
|
||||||
|
return sortAuthor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSortAuthor(@Nullable Author sortAuthor) {
|
||||||
|
this.sortAuthor = sortAuthor;
|
||||||
|
}
|
||||||
|
|
||||||
public void setAvailable(Boolean available) {
|
public void setAvailable(Boolean available) {
|
||||||
this.available = available;
|
this.available = available;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Author {
|
||||||
|
@Nullable
|
||||||
|
@Field(name = "sort_name", type = FieldType.Keyword) private String sortName;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public String getSortName() {
|
||||||
|
return sortName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSortName(@Nullable String sortName) {
|
||||||
|
this.sortName = sortName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user