Skip to content

Commit 30bfee2

Browse files
committed
Custom property names must be used in SourceFilter and source fields.
Original Pull Request #1780 Closes #1778
1 parent f339fda commit 30bfee2

File tree

5 files changed

+118
-39
lines changed

5 files changed

+118
-39
lines changed

Diff for: src/main/java/org/springframework/data/elasticsearch/core/convert/ElasticsearchConverter.java

+12-21
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import org.springframework.data.elasticsearch.core.document.Document;
2020
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
2121
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
22-
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
2322
import org.springframework.data.elasticsearch.core.query.Query;
2423
import org.springframework.data.projection.ProjectionFactory;
2524
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
@@ -64,7 +63,13 @@ default String convertId(Object idValue) {
6463
return idValue.toString();
6564
}
6665

67-
return getConversionService().convert(idValue, String.class);
66+
String converted = getConversionService().convert(idValue, String.class);
67+
68+
if (converted == null) {
69+
return idValue.toString();
70+
}
71+
72+
return converted;
6873
}
6974

7075
/**
@@ -86,29 +91,15 @@ default Document mapObject(@Nullable Object source) {
8691

8792
// region query
8893
/**
89-
* Updates a {@link CriteriaQuery} by renaming the property names in the query to the correct mapped field names and
90-
* the values to the converted values if the {@link ElasticsearchPersistentProperty} for a property has a
94+
* Updates a {@link Query} by renaming the property names in the query to the correct mapped field names and the
95+
* values to the converted values if the {@link ElasticsearchPersistentProperty} for a property has a
9196
* {@link org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentPropertyConverter}. If
92-
* domainClass is null or query is not a {@link CriteriaQuery}, it's a noop.
97+
* domainClass is null it's a noop.
9398
*
94-
* @param query the query that is internally updated
99+
* @param query the query that is internally updated, must not be {@literal null}
95100
* @param domainClass the class of the object that is searched with the query
96101
*/
97-
default void updateQuery(Query query, @Nullable Class<?> domainClass) {
98-
99-
if (domainClass != null && query instanceof CriteriaQuery) {
100-
updateCriteriaQuery((CriteriaQuery) query, domainClass);
101-
}
102-
}
102+
void updateQuery(Query query, @Nullable Class<?> domainClass);
103103

104-
/**
105-
* Updates a {@link CriteriaQuery} by renaming the property names in the query to the correct mapped field names and
106-
* the values to the converted values if the {@link ElasticsearchPersistentProperty} for a property has a
107-
* {@link org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentPropertyConverter}.
108-
*
109-
* @param criteriaQuery the query that is internally updated, must not be {@literal null}
110-
* @param domainClass the class of the object that is searched with the query, must not be {@literal null}
111-
*/
112-
void updateCriteriaQuery(CriteriaQuery criteriaQuery, Class<?> domainClass);
113104
// endregion
114105
}

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

+58-1
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,11 @@
4242
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentPropertyConverter;
4343
import org.springframework.data.elasticsearch.core.query.Criteria;
4444
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
45+
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
4546
import org.springframework.data.elasticsearch.core.query.Field;
47+
import org.springframework.data.elasticsearch.core.query.Query;
4648
import org.springframework.data.elasticsearch.core.query.SeqNoPrimaryTerm;
49+
import org.springframework.data.elasticsearch.core.query.SourceFilter;
4750
import org.springframework.data.mapping.MappingException;
4851
import org.springframework.data.mapping.PersistentPropertyAccessor;
4952
import org.springframework.data.mapping.PreferredConstructor;
@@ -1024,7 +1027,61 @@ private static Collection<?> asCollection(Object source) {
10241027

10251028
// region queries
10261029
@Override
1027-
public void updateCriteriaQuery(CriteriaQuery criteriaQuery, Class<?> domainClass) {
1030+
public void updateQuery(Query query, @Nullable Class<?> domainClass) {
1031+
1032+
Assert.notNull(query, "query must not be null");
1033+
1034+
if (domainClass != null) {
1035+
1036+
updateFieldsAndSourceFilter(query, domainClass);
1037+
1038+
if (query instanceof CriteriaQuery) {
1039+
updateCriteriaQuery((CriteriaQuery) query, domainClass);
1040+
}
1041+
}
1042+
}
1043+
1044+
private void updateFieldsAndSourceFilter(Query query, Class<?> domainClass) {
1045+
1046+
ElasticsearchPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(domainClass);
1047+
1048+
if (persistentEntity != null) {
1049+
List<String> fields = query.getFields();
1050+
1051+
if (!fields.isEmpty()) {
1052+
query.setFields(updateFieldNames(fields, persistentEntity));
1053+
}
1054+
1055+
SourceFilter sourceFilter = query.getSourceFilter();
1056+
1057+
if (sourceFilter != null) {
1058+
1059+
String[] includes = null;
1060+
String[] excludes = null;
1061+
1062+
if (sourceFilter.getIncludes() != null) {
1063+
includes = updateFieldNames(Arrays.asList(sourceFilter.getIncludes()), persistentEntity)
1064+
.toArray(new String[] {});
1065+
}
1066+
1067+
if (sourceFilter.getExcludes() != null) {
1068+
excludes = updateFieldNames(Arrays.asList(sourceFilter.getExcludes()), persistentEntity)
1069+
.toArray(new String[] {});
1070+
}
1071+
1072+
query.addSourceFilter(new FetchSourceFilter(includes, excludes));
1073+
}
1074+
}
1075+
}
1076+
1077+
private List<String> updateFieldNames(List<String> fields, ElasticsearchPersistentEntity<?> persistentEntity) {
1078+
return fields.stream().map(fieldName -> {
1079+
ElasticsearchPersistentProperty persistentProperty = persistentEntity.getPersistentProperty(fieldName);
1080+
return persistentProperty != null ? persistentProperty.getFieldName() : fieldName;
1081+
}).collect(Collectors.toList());
1082+
}
1083+
1084+
private void updateCriteriaQuery(CriteriaQuery criteriaQuery, Class<?> domainClass) {
10281085

10291086
Assert.notNull(criteriaQuery, "criteriaQuery must not be null");
10301087
Assert.notNull(domainClass, "domainClass must not be null");

Diff for: src/main/java/org/springframework/data/elasticsearch/core/query/AbstractQuery.java

+9
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,15 @@ public List<String> getFields() {
9696
return fields;
9797
}
9898

99+
@Override
100+
public void setFields(List<String> fields) {
101+
102+
Assert.notNull(fields, "fields must not be null");
103+
104+
this.fields.clear();
105+
this.fields.addAll(fields);
106+
}
107+
99108
@Override
100109
public void addSourceFilter(SourceFilter sourceFilter) {
101110
this.sourceFilter = sourceFilter;

Diff for: src/main/java/org/springframework/data/elasticsearch/core/query/Query.java

+7
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,13 @@ static Query findAll() {
103103
*/
104104
List<String> getFields();
105105

106+
/**
107+
* Set fields to be returned as part of search request
108+
* @param fields must not be {@literal null}
109+
* @since 4.2.1
110+
*/
111+
void setFields(List<String> fields);
112+
106113
/**
107114
* Add source filter to be added as part of search request
108115
*

Diff for: src/test/java/org/springframework/data/elasticsearch/core/CriteriaQueryMappingUnitTests.java

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

18-
import static org.assertj.core.api.Assertions.*;
1918
import static org.skyscreamer.jsonassert.JSONAssert.*;
2019

2120
import java.time.LocalDate;
@@ -25,6 +24,7 @@
2524
import java.util.List;
2625
import java.util.Objects;
2726

27+
import org.assertj.core.api.SoftAssertions;
2828
import org.json.JSONException;
2929
import org.junit.jupiter.api.BeforeEach;
3030
import org.junit.jupiter.api.DisplayName;
@@ -44,6 +44,9 @@
4444
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
4545
import org.springframework.data.elasticsearch.core.query.Criteria;
4646
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
47+
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
48+
import org.springframework.data.elasticsearch.core.query.Query;
49+
import org.springframework.data.elasticsearch.core.query.SourceFilter;
4750
import org.springframework.lang.Nullable;
4851

4952
/**
@@ -356,9 +359,7 @@ void shouldMapNamesAndValueInNestedEntities() throws JSONException {
356359
" }\n" + //
357360
"}\n"; //
358361

359-
CriteriaQuery criteriaQuery = new CriteriaQuery(
360-
new Criteria("persons.birthDate").is(LocalDate.of(1999, 10, 3))
361-
);
362+
CriteriaQuery criteriaQuery = new CriteriaQuery(new Criteria("persons.birthDate").is(LocalDate.of(1999, 10, 3)));
362363
mappingElasticsearchConverter.updateQuery(criteriaQuery, House.class);
363364
String queryString = new CriteriaQueryProcessor().createQuery(criteriaQuery.getCriteria()).toString();
364365

@@ -389,9 +390,7 @@ void shouldMapNamesAndValueInNestedEntitiesWithSubfields() throws JSONException
389390
" }\n" + //
390391
"}\n"; //
391392

392-
CriteriaQuery criteriaQuery = new CriteriaQuery(
393-
new Criteria("persons.nickName.keyword").is("Foobar")
394-
);
393+
CriteriaQuery criteriaQuery = new CriteriaQuery(new Criteria("persons.nickName.keyword").is("Foobar"));
395394
mappingElasticsearchConverter.updateQuery(criteriaQuery, House.class);
396395
String queryString = new CriteriaQueryProcessor().createQuery(criteriaQuery.getCriteria()).toString();
397396

@@ -417,14 +416,33 @@ void shouldMapNamesAndValueInObjectEntities() throws JSONException {
417416
" }\n" + //
418417
"}\n"; //
419418

420-
CriteriaQuery criteriaQuery = new CriteriaQuery(
421-
new Criteria("persons.birthDate").is(LocalDate.of(1999, 10, 3))
422-
);
419+
CriteriaQuery criteriaQuery = new CriteriaQuery(new Criteria("persons.birthDate").is(LocalDate.of(1999, 10, 3)));
423420
mappingElasticsearchConverter.updateQuery(criteriaQuery, ObjectWithPerson.class);
424421
String queryString = new CriteriaQueryProcessor().createQuery(criteriaQuery.getCriteria()).toString();
425422

426423
assertEquals(expected, queryString, false);
427424
}
425+
426+
@Test // #1778
427+
@DisplayName("should map names in source fields and SourceFilters")
428+
void shouldMapNamesInSourceFieldsAndSourceFilters() {
429+
430+
Query query = Query.findAll();
431+
// Note: we don't care if these filters make sense here, this test is only about name mapping
432+
query.addFields("firstName", "lastName");
433+
query.addSourceFilter(new FetchSourceFilter(new String[] { "firstName" }, new String[] { "lastName" }));
434+
435+
mappingElasticsearchConverter.updateQuery(query, Person.class);
436+
437+
SoftAssertions softly = new SoftAssertions();
438+
softly.assertThat(query.getFields()).containsExactly("first-name", "last-name");
439+
SourceFilter sourceFilter = query.getSourceFilter();
440+
softly.assertThat(sourceFilter).isNotNull();
441+
softly.assertThat(sourceFilter.getIncludes()).containsExactly("first-name");
442+
softly.assertThat(sourceFilter.getExcludes()).containsExactly("last-name");
443+
softly.assertAll();
444+
}
445+
428446
// endregion
429447

430448
// region helper functions
@@ -442,24 +460,21 @@ static class Person {
442460
@Nullable @Id String id;
443461
@Nullable @Field(name = "first-name") String firstName;
444462
@Nullable @Field(name = "last-name") String lastName;
445-
@Nullable @MultiField(mainField = @Field(name="nick-name"), otherFields = {@InnerField(suffix = "keyword", type = FieldType.Keyword)}) String nickName;
463+
@Nullable @MultiField(mainField = @Field(name = "nick-name"),
464+
otherFields = { @InnerField(suffix = "keyword", type = FieldType.Keyword) }) String nickName;
446465
@Nullable @Field(name = "created-date", type = FieldType.Date, format = DateFormat.epoch_millis) Date createdDate;
447466
@Nullable @Field(name = "birth-date", type = FieldType.Date, format = {},
448467
pattern = "dd.MM.uuuu") LocalDate birthDate;
449468
}
450469

451470
static class House {
452471
@Nullable @Id String id;
453-
@Nullable
454-
@Field(name = "per-sons", type = FieldType.Nested)
455-
List<Person> persons;
472+
@Nullable @Field(name = "per-sons", type = FieldType.Nested) List<Person> persons;
456473
}
457474

458475
static class ObjectWithPerson {
459476
@Nullable @Id String id;
460-
@Nullable
461-
@Field(name = "per-sons", type = FieldType.Object)
462-
List<Person> persons;
477+
@Nullable @Field(name = "per-sons", type = FieldType.Object) List<Person> persons;
463478
}
464479

465480
static class GeoShapeEntity {

0 commit comments

Comments
 (0)