Skip to content

Commit 4294ace

Browse files
committed
Add native support for range field types by using a range object
1 parent 679f99a commit 4294ace

13 files changed

+1203
-27
lines changed

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

+444
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/*
2+
* Copyright 2021 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 java.util.LinkedHashMap;
19+
import java.util.Map;
20+
21+
import org.springframework.data.elasticsearch.core.Range;
22+
import org.springframework.data.mapping.PersistentProperty;
23+
import org.springframework.util.Assert;
24+
25+
/**
26+
* @author Sascha Woo
27+
* @since 4.3
28+
*/
29+
public abstract class AbstractRangePersistentPropertyConverter<T> extends AbstractPersistentPropertyConverter {
30+
31+
protected static final String LT_FIELD = "lt";
32+
protected static final String LTE_FIELD = "lte";
33+
protected static final String GT_FIELD = "gt";
34+
protected static final String GTE_FIELD = "gte";
35+
36+
public AbstractRangePersistentPropertyConverter(PersistentProperty<?> property) {
37+
super(property);
38+
}
39+
40+
@Override
41+
public Object read(Object value) {
42+
43+
Assert.notNull(value, "value must not be null.");
44+
Assert.isInstanceOf(Map.class, value, "value must be instance of Map.");
45+
46+
try {
47+
Map<String, Object> source = (Map<String, Object>) value;
48+
Range.Bound<T> lowerBound;
49+
Range.Bound<T> upperBound;
50+
51+
if (source.containsKey(GTE_FIELD)) {
52+
lowerBound = Range.Bound.inclusive(parse((String) source.get(GTE_FIELD)));
53+
} else if (source.containsKey(GT_FIELD)) {
54+
lowerBound = Range.Bound.exclusive(parse((String) source.get(GT_FIELD)));
55+
} else {
56+
lowerBound = Range.Bound.unbounded();
57+
}
58+
59+
if (source.containsKey(LTE_FIELD)) {
60+
upperBound = Range.Bound.inclusive(parse((String) source.get(LTE_FIELD)));
61+
} else if (source.containsKey(LT_FIELD)) {
62+
upperBound = Range.Bound.exclusive(parse((String) source.get(LT_FIELD)));
63+
} else {
64+
upperBound = Range.Bound.unbounded();
65+
}
66+
67+
return Range.of(lowerBound, upperBound);
68+
69+
} catch (Exception e) {
70+
throw new ConversionException(
71+
String.format("Unable to convert value '%s' of property '%s'", value, getProperty().getName()), e);
72+
}
73+
}
74+
75+
@Override
76+
public Object write(Object value) {
77+
78+
Assert.notNull(value, "value must not be null.");
79+
Assert.isInstanceOf(Range.class, value, "value must be instance of Range.");
80+
81+
try {
82+
Range<T> range = (Range<T>) value;
83+
Range.Bound<T> lowerBound = range.getLowerBound();
84+
Range.Bound<T> upperBound = range.getUpperBound();
85+
Map<String, Object> target = new LinkedHashMap<>();
86+
87+
if (lowerBound.isBounded()) {
88+
String lowerBoundValue = format(lowerBound.getValue().get());
89+
if (lowerBound.isInclusive()) {
90+
target.put(GTE_FIELD, lowerBoundValue);
91+
} else {
92+
target.put(GT_FIELD, lowerBoundValue);
93+
}
94+
}
95+
96+
if (upperBound.isBounded()) {
97+
String upperBoundValue = format(upperBound.getValue().get());
98+
if (upperBound.isInclusive()) {
99+
target.put(LTE_FIELD, upperBoundValue);
100+
} else {
101+
target.put(LT_FIELD, upperBoundValue);
102+
}
103+
}
104+
105+
return target;
106+
107+
} catch (Exception e) {
108+
throw new ConversionException(
109+
String.format("Unable to convert value '%s' of property '%s'", value, getProperty().getName()), e);
110+
}
111+
}
112+
113+
protected abstract String format(T value);
114+
115+
protected Class<?> getGenericType() {
116+
return getProperty().getTypeInformation().getTypeArguments().get(0).getType();
117+
}
118+
119+
protected abstract T parse(String value);
120+
121+
}

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

+6-4
Original file line numberDiff line numberDiff line change
@@ -40,22 +40,24 @@ public DatePersistentPropertyConverter(PersistentProperty<?> property,
4040
}
4141

4242
@Override
43-
public Object read(String value) {
43+
public Object read(Object value) {
44+
45+
String s = value.toString();
4446

4547
for (ElasticsearchDateConverter dateConverter : dateConverters) {
4648
try {
47-
return dateConverter.parse(value);
49+
return dateConverter.parse(s);
4850
} catch (Exception e) {
4951
LOGGER.trace(e.getMessage(), e);
5052
}
5153
}
5254

53-
throw new ConversionException(String.format("Unable to convert value '%s' to %s for property '%s'", value,
55+
throw new ConversionException(String.format("Unable to convert value '%s' to %s for property '%s'", s,
5456
getProperty().getActualType().getTypeName(), getProperty().getName()));
5557
}
5658

5759
@Override
58-
public String write(Object value) {
60+
public Object write(Object value) {
5961

6062
try {
6163
return dateConverters.get(0).format((Date) value);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright 2021 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 java.util.Date;
19+
import java.util.List;
20+
21+
import org.slf4j.Logger;
22+
import org.slf4j.LoggerFactory;
23+
import org.springframework.data.mapping.PersistentProperty;
24+
25+
/**
26+
* @author Sascha Woo
27+
* @since 4.3
28+
*/
29+
public class DateRangePersistentPropertyConverter extends AbstractRangePersistentPropertyConverter<Date> {
30+
31+
private static final Logger LOGGER = LoggerFactory.getLogger(DateRangePersistentPropertyConverter.class);
32+
33+
private final List<ElasticsearchDateConverter> dateConverters;
34+
35+
public DateRangePersistentPropertyConverter(PersistentProperty<?> property,
36+
List<ElasticsearchDateConverter> dateConverters) {
37+
38+
super(property);
39+
this.dateConverters = dateConverters;
40+
}
41+
42+
@Override
43+
protected String format(Date value) {
44+
return dateConverters.get(0).format(value);
45+
}
46+
47+
@Override
48+
protected Date parse(String value) {
49+
50+
for (ElasticsearchDateConverter converters : dateConverters) {
51+
try {
52+
return converters.parse(value);
53+
} catch (Exception e) {
54+
LOGGER.trace(e.getMessage(), e);
55+
}
56+
}
57+
58+
throw new ConversionException(String.format("Unable to convert value '%s' to %s for property '%s'", value,
59+
getGenericType().getTypeName(), getProperty().getName()));
60+
}
61+
62+
}

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

+1-4
Original file line numberDiff line numberDiff line change
@@ -482,10 +482,7 @@ private Object propertyConverterRead(ElasticsearchPersistentProperty property, O
482482
}
483483

484484
private Object convertOnRead(ElasticsearchPersistentPropertyConverter propertyConverter, Object source) {
485-
if (String.class.isAssignableFrom(source.getClass())) {
486-
source = propertyConverter.read((String) source);
487-
}
488-
return source;
485+
return propertyConverter.read(source);
489486
}
490487

491488
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright 2021 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.data.mapping.PersistentProperty;
19+
20+
/**
21+
* @author Sascha Woo
22+
* @since 4.3
23+
*/
24+
public class NumberRangePersistentPropertyConverter extends AbstractRangePersistentPropertyConverter<Number> {
25+
26+
public NumberRangePersistentPropertyConverter(PersistentProperty<?> property) {
27+
super(property);
28+
}
29+
30+
@Override
31+
protected String format(Number number) {
32+
return String.valueOf(number);
33+
}
34+
35+
@Override
36+
protected Number parse(String value) {
37+
38+
Class<?> type = getGenericType();
39+
if (Integer.class.isAssignableFrom(type)) {
40+
return Integer.valueOf(value);
41+
} else if (Float.class.isAssignableFrom(type)) {
42+
return Float.valueOf(value);
43+
} else if (Long.class.isAssignableFrom(type)) {
44+
return Long.valueOf(value);
45+
} else if (Double.class.isAssignableFrom(type)) {
46+
return Double.valueOf(value);
47+
}
48+
49+
throw new ConversionException(String.format("Unable to convert value '%s' to %s for property '%s'", value,
50+
type.getTypeName(), getProperty().getName()));
51+
}
52+
53+
}

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

+5-4
Original file line numberDiff line numberDiff line change
@@ -41,24 +41,25 @@ public TemporalPersistentPropertyConverter(PersistentProperty<?> property,
4141

4242
@SuppressWarnings("unchecked")
4343
@Override
44-
public Object read(String value) {
44+
public Object read(Object value) {
4545

46+
String s = value.toString();
4647
Class<?> actualType = getProperty().getActualType();
4748

4849
for (ElasticsearchDateConverter dateConverter : dateConverters) {
4950
try {
50-
return dateConverter.parse(value, (Class<? extends TemporalAccessor>) actualType);
51+
return dateConverter.parse(s, (Class<? extends TemporalAccessor>) actualType);
5152
} catch (Exception e) {
5253
LOGGER.trace(e.getMessage(), e);
5354
}
5455
}
5556

56-
throw new ConversionException(String.format("Unable to convert value '%s' to %s for property '%s'", value,
57+
throw new ConversionException(String.format("Unable to convert value '%s' to %s for property '%s'", s,
5758
getProperty().getActualType().getTypeName(), getProperty().getName()));
5859
}
5960

6061
@Override
61-
public String write(Object value) {
62+
public Object write(Object value) {
6263

6364
try {
6465
return dateConverters.get(0).format((TemporalAccessor) value);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright 2021 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 java.time.temporal.TemporalAccessor;
19+
import java.util.List;
20+
21+
import org.slf4j.Logger;
22+
import org.slf4j.LoggerFactory;
23+
import org.springframework.data.mapping.PersistentProperty;
24+
import org.springframework.util.Assert;
25+
26+
/**
27+
* @author Sascha Woo
28+
* @since 4.3
29+
*/
30+
public class TemporalRangePersistentPropertyConverter
31+
extends AbstractRangePersistentPropertyConverter<TemporalAccessor> {
32+
33+
private static final Logger LOGGER = LoggerFactory.getLogger(TemporalRangePersistentPropertyConverter.class);
34+
35+
private final List<ElasticsearchDateConverter> dateConverters;
36+
37+
public TemporalRangePersistentPropertyConverter(PersistentProperty<?> property,
38+
List<ElasticsearchDateConverter> dateConverters) {
39+
40+
super(property);
41+
42+
Assert.notEmpty(dateConverters, "dateConverters must not be empty.");
43+
this.dateConverters = dateConverters;
44+
}
45+
46+
@Override
47+
protected String format(TemporalAccessor temporal) {
48+
return dateConverters.get(0).format(temporal);
49+
}
50+
51+
@Override
52+
protected TemporalAccessor parse(String value) {
53+
54+
Class<?> type = getGenericType();
55+
for (ElasticsearchDateConverter converters : dateConverters) {
56+
try {
57+
return converters.parse(value, (Class<? extends TemporalAccessor>) type);
58+
} catch (Exception e) {
59+
LOGGER.trace(e.getMessage(), e);
60+
}
61+
}
62+
63+
throw new ConversionException(String.format("Unable to convert value '%s' to %s for property '%s'", value,
64+
type.getTypeName(), getProperty().getName()));
65+
}
66+
67+
}

0 commit comments

Comments
 (0)