Skip to content

Commit 22d7405

Browse files
committed
Fix cutting of unknown properties in property paths for search.
Closes spring-projects#3081 Signed-off-by: Peter-Josef Meisch <[email protected]>
1 parent 2366f67 commit 22d7405

File tree

2 files changed

+101
-57
lines changed

2 files changed

+101
-57
lines changed

src/main/java/org/springframework/data/elasticsearch/core/convert/MappingElasticsearchConverter.java

+59-57
Original file line numberDiff line numberDiff line change
@@ -1372,53 +1372,21 @@ private void updatePropertiesInCriteria(Criteria criteria, ElasticsearchPersiste
13721372
return;
13731373
}
13741374

1375-
String[] fieldNames = field.getName().split("\\.");
1376-
1377-
ElasticsearchPersistentEntity<?> currentEntity = persistentEntity;
1378-
ElasticsearchPersistentProperty persistentProperty = null;
1379-
int propertyCount = 0;
1380-
boolean isNested = false;
1381-
1382-
for (int i = 0; i < fieldNames.length; i++) {
1383-
persistentProperty = currentEntity.getPersistentProperty(fieldNames[i]);
1384-
1385-
if (persistentProperty != null) {
1386-
propertyCount++;
1387-
fieldNames[i] = persistentProperty.getFieldName();
1388-
1389-
org.springframework.data.elasticsearch.annotations.Field fieldAnnotation = persistentProperty
1390-
.findAnnotation(org.springframework.data.elasticsearch.annotations.Field.class);
1391-
1392-
if (fieldAnnotation != null && fieldAnnotation.type() == FieldType.Nested) {
1393-
isNested = true;
1394-
}
1395-
1396-
try {
1397-
currentEntity = mappingContext.getPersistentEntity(persistentProperty.getActualType());
1398-
} catch (Exception e) {
1399-
// using system types like UUIDs will lead to java.lang.reflect.InaccessibleObjectException in JDK 16
1400-
// so if we cannot get an entity here, bail out.
1401-
currentEntity = null;
1402-
}
1403-
}
1404-
1405-
if (currentEntity == null) {
1406-
break;
1407-
}
1408-
}
1375+
var propertyNamesUpdate = updatePropertyNames(persistentEntity, field.getName());
14091376

1377+
var fieldNames = propertyNamesUpdate.names();
14101378
field.setName(String.join(".", fieldNames));
14111379

1412-
if (propertyCount > 1 && isNested) {
1380+
if (propertyNamesUpdate.propertyCount() > 1 && propertyNamesUpdate.nestedProperty()) {
14131381
List<String> propertyNames = Arrays.asList(fieldNames);
1414-
field.setPath(String.join(".", propertyNames.subList(0, propertyCount - 1)));
1382+
field.setPath(String.join(".", propertyNames.subList(0, propertyNamesUpdate.propertyCount - 1)));
14151383
}
14161384

1417-
if (persistentProperty != null) {
1385+
if (propertyNamesUpdate.persistentProperty != null) {
14181386

1419-
if (persistentProperty.hasPropertyValueConverter()) {
1387+
if (propertyNamesUpdate.persistentProperty.hasPropertyValueConverter()) {
14201388
PropertyValueConverter propertyValueConverter = Objects
1421-
.requireNonNull(persistentProperty.getPropertyValueConverter());
1389+
.requireNonNull(propertyNamesUpdate.persistentProperty.getPropertyValueConverter());
14221390
criteria.getQueryCriteriaEntries().forEach(criteriaEntry -> {
14231391

14241392
if (criteriaEntry.getKey().hasValue()) {
@@ -1437,7 +1405,7 @@ private void updatePropertiesInCriteria(Criteria criteria, ElasticsearchPersiste
14371405
});
14381406
}
14391407

1440-
org.springframework.data.elasticsearch.annotations.Field fieldAnnotation = persistentProperty
1408+
org.springframework.data.elasticsearch.annotations.Field fieldAnnotation = propertyNamesUpdate.persistentProperty
14411409
.findAnnotation(org.springframework.data.elasticsearch.annotations.Field.class);
14421410

14431411
if (fieldAnnotation != null) {
@@ -1446,36 +1414,70 @@ private void updatePropertiesInCriteria(Criteria criteria, ElasticsearchPersiste
14461414
}
14471415
}
14481416

1417+
static record PropertyNamesUpdate(
1418+
String[] names,
1419+
Boolean nestedProperty,
1420+
Integer propertyCount,
1421+
ElasticsearchPersistentProperty persistentProperty) {
1422+
}
1423+
14491424
@Override
14501425
public String updateFieldNames(String propertyPath, ElasticsearchPersistentEntity<?> persistentEntity) {
14511426

14521427
Assert.notNull(propertyPath, "propertyPath must not be null");
14531428
Assert.notNull(persistentEntity, "persistentEntity must not be null");
14541429

1455-
var properties = propertyPath.split("\\.", 2);
1430+
var propertyNamesUpdate = updatePropertyNames(persistentEntity, propertyPath);
1431+
return String.join(".", propertyNamesUpdate.names());
1432+
}
14561433

1457-
if (properties.length > 0) {
1458-
var propertyName = properties[0];
1459-
var fieldName = propertyToFieldName(persistentEntity, propertyName);
1434+
/**
1435+
* Parse a propertyPath and replace the path values with the field names from a persistentEntity. path entries not
1436+
* found in the entity are kept as they are.
1437+
*
1438+
* @return the eventually modified names, a flag if a nested entity was encountered the number of processed
1439+
* propertiesand the last processed PersistentProperty.
1440+
*/
1441+
PropertyNamesUpdate updatePropertyNames(ElasticsearchPersistentEntity<?> persistentEntity, String propertyPath) {
14601442

1461-
if (properties.length > 1) {
1462-
var persistentProperty = persistentEntity.getPersistentProperty(propertyName);
1443+
String[] propertyNames = propertyPath.split("\\.");
1444+
String[] fieldNames = Arrays.copyOf(propertyNames, propertyNames.length);
14631445

1464-
if (persistentProperty != null) {
1465-
ElasticsearchPersistentEntity<?> nestedPersistentEntity = mappingContext
1466-
.getPersistentEntity(persistentProperty);
1467-
if (nestedPersistentEntity != null) {
1468-
return fieldName + '.' + updateFieldNames(properties[1], nestedPersistentEntity);
1469-
} else {
1470-
return fieldName;
1471-
}
1446+
ElasticsearchPersistentEntity<?> currentEntity = persistentEntity;
1447+
ElasticsearchPersistentProperty persistentProperty = null;
1448+
1449+
int propertyCount = 0;
1450+
boolean isNested = false;
1451+
1452+
for (int i = 0; i < propertyNames.length; i++) {
1453+
persistentProperty = currentEntity.getPersistentProperty(propertyNames[i]);
1454+
1455+
if (persistentProperty != null) {
1456+
propertyCount++;
1457+
fieldNames[i] = persistentProperty.getFieldName();
1458+
1459+
org.springframework.data.elasticsearch.annotations.Field fieldAnnotation = persistentProperty
1460+
.findAnnotation(org.springframework.data.elasticsearch.annotations.Field.class);
1461+
1462+
if (fieldAnnotation != null && fieldAnnotation.type() == FieldType.Nested) {
1463+
isNested = true;
1464+
}
1465+
1466+
try {
1467+
currentEntity = mappingContext.getPersistentEntity(persistentProperty.getActualType());
1468+
} catch (Exception e) {
1469+
// using system types like UUIDs will lead to java.lang.reflect.InaccessibleObjectException in JDK 16
1470+
// so if we cannot get an entity here, bail out.
1471+
currentEntity = null;
14721472
}
14731473
}
1474-
return fieldName;
1475-
} else {
1476-
return propertyPath;
1474+
1475+
if (currentEntity == null) {
1476+
break;
1477+
}
14771478
}
14781479

1480+
return new PropertyNamesUpdate(fieldNames, isNested, propertyCount, persistentProperty);
14791481
}
14801482
// endregion
14811483

src/test/java/org/springframework/data/elasticsearch/core/query/RepositoryPartQueryIntegrationTests.java

+42
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package org.springframework.data.elasticsearch.core.query;
1717

18+
import static org.assertj.core.api.Assertions.*;
1819
import static org.skyscreamer.jsonassert.JSONAssert.*;
1920

2021
import java.lang.reflect.Method;
@@ -27,6 +28,7 @@
2728
import org.junit.jupiter.api.Test;
2829
import org.springframework.beans.factory.annotation.Autowired;
2930
import org.springframework.data.annotation.Id;
31+
import org.springframework.data.domain.Sort;
3032
import org.springframework.data.elasticsearch.annotations.Field;
3133
import org.springframework.data.elasticsearch.annotations.FieldType;
3234
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
@@ -679,6 +681,45 @@ void shouldBuildSortObjectWithCorrectFieldNames() throws NoSuchMethodException,
679681
assertEquals(expected, query, false);
680682
}
681683

684+
@Test // #3081
685+
@DisplayName("should build sort object with unknown field names")
686+
void shouldBuildSortObjectWithUnknownFieldNames() throws NoSuchMethodException, JSONException {
687+
688+
String methodName = "findByName";
689+
Class<?>[] parameterClasses = new Class[] { String.class, Sort.class };
690+
Object[] parameters = new Object[] { BOOK_TITLE, Sort.by("sortAuthor.sortName.raw") };
691+
692+
String query = getQueryString(methodName, parameterClasses, parameters);
693+
694+
String expected = """
695+
696+
{
697+
"query": {
698+
"bool": {
699+
"must": [
700+
{
701+
"query_string": {
702+
"query": "Title",
703+
"fields": [
704+
"name"
705+
]
706+
}
707+
}
708+
]
709+
}
710+
},
711+
"sort": [
712+
{
713+
"sort_author.sort_name.raw": {
714+
"order": "asc"
715+
}
716+
}
717+
]
718+
}""";
719+
720+
assertEquals(expected, query, false);
721+
}
722+
682723
private String getQueryString(String methodName, Class<?>[] parameterClasses, Object[] parameters)
683724
throws NoSuchMethodException {
684725

@@ -768,6 +809,7 @@ private interface SampleRepository extends ElasticsearchRepository<Book, String>
768809

769810
List<Book> findByNameOrderBySortAuthor_SortName(String name);
770811

812+
List<Book> findByName(String name, Sort sort);
771813
}
772814

773815
public static class Book {

0 commit comments

Comments
 (0)