Skip to content

Commit 6d51e67

Browse files
authored
Introduce MappingConversionException.
Original Pull Request #2882 Closes #2879
1 parent 0a51dba commit 6d51e67

File tree

3 files changed

+111
-43
lines changed

3 files changed

+111
-43
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.elasticsearch.core.convert;
17+
18+
import org.springframework.lang.Nullable;
19+
20+
/**
21+
* @since 5.3
22+
* @author Peter-Josef Meisch
23+
*/
24+
public class MappingConversionException extends RuntimeException {
25+
private final String documentId;
26+
27+
public MappingConversionException(@Nullable String documentId, Throwable cause) {
28+
super(cause);
29+
this.documentId = documentId != null ? documentId : "\"null\"";
30+
}
31+
32+
public String getDocumentId() {
33+
return documentId;
34+
}
35+
36+
@Override
37+
public String getMessage() {
38+
return "Conversion exception when converting document id " + documentId;
39+
}
40+
}

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

Lines changed: 41 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@
6565
import org.springframework.util.CollectionUtils;
6666
import org.springframework.util.ObjectUtils;
6767

68+
import javax.print.Doc;
69+
6870
/**
6971
* Elasticsearch specific {@link org.springframework.data.convert.EntityConverter} implementation based on domain type
7072
* {@link ElasticsearchPersistentEntity metadata}.
@@ -333,46 +335,52 @@ private <R> R readEntity(ElasticsearchPersistentEntity<?> entity, Map<String, Ob
333335
return instance;
334336
}
335337

338+
Document document = (source instanceof Document) ? (Document) source : null;
339+
336340
ElasticsearchPropertyValueProvider valueProvider = new ElasticsearchPropertyValueProvider(accessor, evaluator);
337-
R result = readProperties(targetEntity, instance, valueProvider);
338-
339-
if (source instanceof Document document) {
340-
if (document.hasId()) {
341-
ElasticsearchPersistentProperty idProperty = targetEntity.getIdProperty();
342-
PersistentPropertyAccessor<R> propertyAccessor = new ConvertingPropertyAccessor<>(
343-
targetEntity.getPropertyAccessor(result), conversionService);
344-
// Only deal with String because ES generated Ids are strings !
345-
if (idProperty != null && idProperty.isReadable() && idProperty.getType().isAssignableFrom(String.class)) {
346-
propertyAccessor.setProperty(idProperty, document.getId());
341+
try {
342+
R result = readProperties(targetEntity, instance, valueProvider);
343+
344+
if (document != null) {
345+
if (document.hasId()) {
346+
ElasticsearchPersistentProperty idProperty = targetEntity.getIdProperty();
347+
PersistentPropertyAccessor<R> propertyAccessor = new ConvertingPropertyAccessor<>(
348+
targetEntity.getPropertyAccessor(result), conversionService);
349+
// Only deal with String because ES generated Ids are strings !
350+
if (idProperty != null && idProperty.isReadable() && idProperty.getType().isAssignableFrom(String.class)) {
351+
propertyAccessor.setProperty(idProperty, document.getId());
352+
}
347353
}
348-
}
349354

350-
if (document.hasVersion()) {
351-
long version = document.getVersion();
352-
ElasticsearchPersistentProperty versionProperty = targetEntity.getVersionProperty();
353-
// Only deal with Long because ES versions are longs !
354-
if (versionProperty != null && versionProperty.getType().isAssignableFrom(Long.class)) {
355-
// check that a version was actually returned in the response, -1 would indicate that
356-
// a search didn't request the version ids in the response, which would be an issue
357-
Assert.isTrue(version != -1, "Version in response is -1");
358-
targetEntity.getPropertyAccessor(result).setProperty(versionProperty, version);
355+
if (document.hasVersion()) {
356+
long version = document.getVersion();
357+
ElasticsearchPersistentProperty versionProperty = targetEntity.getVersionProperty();
358+
// Only deal with Long because ES versions are longs !
359+
if (versionProperty != null && versionProperty.getType().isAssignableFrom(Long.class)) {
360+
// check that a version was actually returned in the response, -1 would indicate that
361+
// a search didn't request the version ids in the response, which would be an issue
362+
Assert.isTrue(version != -1, "Version in response is -1");
363+
targetEntity.getPropertyAccessor(result).setProperty(versionProperty, version);
364+
}
359365
}
360-
}
361366

362-
if (targetEntity.hasSeqNoPrimaryTermProperty() && document.hasSeqNo() && document.hasPrimaryTerm()) {
363-
if (isAssignedSeqNo(document.getSeqNo()) && isAssignedPrimaryTerm(document.getPrimaryTerm())) {
364-
SeqNoPrimaryTerm seqNoPrimaryTerm = new SeqNoPrimaryTerm(document.getSeqNo(), document.getPrimaryTerm());
365-
ElasticsearchPersistentProperty property = targetEntity.getRequiredSeqNoPrimaryTermProperty();
366-
targetEntity.getPropertyAccessor(result).setProperty(property, seqNoPrimaryTerm);
367+
if (targetEntity.hasSeqNoPrimaryTermProperty() && document.hasSeqNo() && document.hasPrimaryTerm()) {
368+
if (isAssignedSeqNo(document.getSeqNo()) && isAssignedPrimaryTerm(document.getPrimaryTerm())) {
369+
SeqNoPrimaryTerm seqNoPrimaryTerm = new SeqNoPrimaryTerm(document.getSeqNo(), document.getPrimaryTerm());
370+
ElasticsearchPersistentProperty property = targetEntity.getRequiredSeqNoPrimaryTermProperty();
371+
targetEntity.getPropertyAccessor(result).setProperty(property, seqNoPrimaryTerm);
372+
}
367373
}
368374
}
369-
}
370375

371-
if (source instanceof SearchDocument searchDocument) {
372-
populateScriptFields(targetEntity, result, searchDocument);
376+
if (source instanceof SearchDocument searchDocument) {
377+
populateScriptFields(targetEntity, result, searchDocument);
378+
}
379+
return result;
380+
} catch (ConversionException e) {
381+
String documentId = (document != null && document.hasId()) ? document.getId() : null;
382+
throw new MappingConversionException(documentId, e);
373383
}
374-
375-
return result;
376384
}
377385

378386
private ParameterValueProvider<ElasticsearchPersistentProperty> getParameterProvider(
@@ -510,11 +518,9 @@ private Object propertyConverterRead(ElasticsearchPersistentProperty property, O
510518
}
511519

512520
if (source instanceof List<?> list) {
513-
source = list.stream().map(it -> convertOnRead(propertyValueConverter, it))
514-
.collect(Collectors.toList());
521+
source = list.stream().map(it -> convertOnRead(propertyValueConverter, it)).collect(Collectors.toList());
515522
} else if (source instanceof Set<?> set) {
516-
source = set.stream().map(it -> convertOnRead(propertyValueConverter, it))
517-
.collect(Collectors.toSet());
523+
source = set.stream().map(it -> convertOnRead(propertyValueConverter, it)).collect(Collectors.toSet());
518524
} else {
519525
source = convertOnRead(propertyValueConverter, source);
520526
}

src/test/java/org/springframework/data/elasticsearch/core/convert/MappingElasticsearchConverterUnitTests.java

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,13 @@ public void init() {
228228
"org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverterUnitTests$Notification");
229229
}
230230

231+
private Map<String, Object> writeToMap(Object source) {
232+
233+
Document sink = Document.create();
234+
mappingElasticsearchConverter.write(source, sink);
235+
return sink;
236+
}
237+
231238
@Test
232239
public void shouldFailToInitializeGivenMappingContextIsNull() {
233240

@@ -993,7 +1000,7 @@ class RangeTests {
9931000
"nullRange": null,
9941001
"integerRangeList": [
9951002
{
996-
"gte": "2",
1003+
"gte": "2",
9971004
"lte": "5"
9981005
}
9991006
]
@@ -1178,11 +1185,13 @@ public List<Range<Integer>> getIntegerRangeList() {
11781185
public void setIntegerRangeList(List<Range<Integer>> integerRangeList) {
11791186
this.integerRangeList = integerRangeList;
11801187
}
1188+
11811189
}
11821190
}
11831191

11841192
@Nested
11851193
class GeoJsonUnitTests {
1194+
11861195
private GeoJsonEntity entity;
11871196

11881197
@BeforeEach
@@ -1476,6 +1485,7 @@ void shouldReadGeoJsonProperties() {
14761485

14771486
assertThat(entity).isEqualTo(mapped);
14781487
}
1488+
14791489
}
14801490

14811491
@Test // #1454
@@ -1942,13 +1952,6 @@ void shouldReadAnObjectArrayIntoASetPropertyImmutable() {
19421952
assertThat(names).containsExactlyInAnyOrder("child1", "child2");
19431953
}
19441954

1945-
private Map<String, Object> writeToMap(Object source) {
1946-
1947-
Document sink = Document.create();
1948-
mappingElasticsearchConverter.write(source, sink);
1949-
return sink;
1950-
}
1951-
19521955
@Test // #2364
19531956
@DisplayName("should not write id property to document source if configured so")
19541957
void shouldNotWriteIdPropertyToDocumentSourceIfConfiguredSo() throws JSONException {
@@ -2078,6 +2081,25 @@ void shouldMapPropertyPathToFieldNames() {
20782081
assertThat(mappedNames).isEqualTo("level-one.level-two.key-word");
20792082
}
20802083

2084+
@Test // #2879
2085+
@DisplayName("should throw MappingConversionException with document id on reading error")
2086+
void shouldThrowMappingConversionExceptionWithDocumentIdOnReadingError() {
2087+
2088+
@Language("JSON")
2089+
String json = """
2090+
{
2091+
"birth-date": "this-is-not-a-local-date"
2092+
}""";
2093+
2094+
Document document = Document.parse(json);
2095+
document.setId("42");
2096+
2097+
assertThatThrownBy(() -> {
2098+
mappingElasticsearchConverter.read(Person.class, document);
2099+
}).isInstanceOf(MappingConversionException.class).hasFieldOrPropertyWithValue("documentId", "42")
2100+
.hasCauseInstanceOf(ConversionException.class);
2101+
}
2102+
20812103
// region entities
20822104
public static class Sample {
20832105
@Nullable public @ReadOnlyProperty String readOnly;

0 commit comments

Comments
 (0)