DATAMONGO-2315 - Fix $date parameter binding for string based queries.
Original pull request: #772.
This commit is contained in:
committed by
Mark Paluch
parent
296e97903d
commit
8ca16e1cb4
@@ -45,10 +45,12 @@ class DateTimeFormatter {
|
||||
static {
|
||||
FormatterImpl dateTimeHelper;
|
||||
try {
|
||||
dateTimeHelper = loadDateTimeFormatter("org.bson.json.DateTimeFormatter$Java8DateTimeFormatter");
|
||||
dateTimeHelper = loadDateTimeFormatter(
|
||||
"org.springframework.data.mongodb.util.json.DateTimeFormatter$Java8DateTimeFormatter");
|
||||
} catch (LinkageError e) {
|
||||
// this is expected if running on a release prior to Java 8: fallback to JAXB.
|
||||
dateTimeHelper = loadDateTimeFormatter("org.bson.json.DateTimeFormatter$JaxbDateTimeFormatter");
|
||||
dateTimeHelper = loadDateTimeFormatter(
|
||||
"org.springframework.data.mongodb.util.json.DateTimeFormatter$JaxbDateTimeFormatter");
|
||||
}
|
||||
|
||||
FORMATTER_IMPL = dateTimeHelper;
|
||||
|
||||
@@ -21,6 +21,7 @@ import static org.bson.codecs.configuration.CodecRegistries.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
@@ -35,22 +36,13 @@ import org.bson.BsonValue;
|
||||
import org.bson.BsonWriter;
|
||||
import org.bson.Document;
|
||||
import org.bson.Transformer;
|
||||
import org.bson.codecs.BsonTypeClassMap;
|
||||
import org.bson.codecs.BsonTypeCodecMap;
|
||||
import org.bson.codecs.BsonValueCodecProvider;
|
||||
import org.bson.codecs.Codec;
|
||||
import org.bson.codecs.CollectibleCodec;
|
||||
import org.bson.codecs.DecoderContext;
|
||||
import org.bson.codecs.DocumentCodecProvider;
|
||||
import org.bson.codecs.EncoderContext;
|
||||
import org.bson.codecs.IdGenerator;
|
||||
import org.bson.codecs.ObjectIdGenerator;
|
||||
import org.bson.codecs.ValueCodecProvider;
|
||||
import org.bson.codecs.*;
|
||||
import org.bson.codecs.configuration.CodecRegistry;
|
||||
|
||||
import org.springframework.data.spel.EvaluationContextProvider;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.NumberUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -278,6 +270,17 @@ public class ParameterBindingDocumentCodec implements CollectibleCodec<Document>
|
||||
if (bindingReader.currentValue != null) {
|
||||
|
||||
Object value = bindingReader.currentValue;
|
||||
|
||||
if (ObjectUtils.nullSafeEquals(BsonType.DATE_TIME, bindingReader.getCurrentBsonType())
|
||||
&& !(value instanceof Date)) {
|
||||
|
||||
if (value instanceof Number) {
|
||||
value = new Date(NumberUtils.convertNumberToTargetClass((Number) value, Long.class));
|
||||
} else if (value instanceof String) {
|
||||
value = new Date(DateTimeFormatter.parse((String) value));
|
||||
}
|
||||
}
|
||||
|
||||
bindingReader.setState(State.TYPE);
|
||||
bindingReader.currentValue = null;
|
||||
return value;
|
||||
|
||||
@@ -394,7 +394,7 @@ public class ParameterBindingJsonReader extends AbstractBsonReader {
|
||||
matched = true;
|
||||
String group = matcher.group();
|
||||
int index = computeParameterIndex(group);
|
||||
computedValue = computedValue.replace(group, ObjectUtils.nullSafeToString(getBindableValueForIndex(index)));
|
||||
computedValue = computedValue.replace(group, nullSaveToString(getBindableValueForIndex(index)));
|
||||
}
|
||||
|
||||
if (!matched) {
|
||||
@@ -406,7 +406,7 @@ public class ParameterBindingJsonReader extends AbstractBsonReader {
|
||||
String binding = regexMatcher.group();
|
||||
String expression = binding.substring(3, binding.length() - 1);
|
||||
|
||||
computedValue = computedValue.replace(binding, ObjectUtils.nullSafeToString(evaluateExpression(expression)));
|
||||
computedValue = computedValue.replace(binding, nullSaveToString(evaluateExpression(expression)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -416,6 +416,15 @@ public class ParameterBindingJsonReader extends AbstractBsonReader {
|
||||
return bindableValue;
|
||||
}
|
||||
|
||||
private static String nullSaveToString(Object value) {
|
||||
|
||||
if (value instanceof Date) {
|
||||
return DateTimeFormatter.format(((Date) value).getTime());
|
||||
}
|
||||
|
||||
return ObjectUtils.nullSafeToString(value);
|
||||
}
|
||||
|
||||
private static int computeParameterIndex(String parameter) {
|
||||
return NumberUtils.parseNumber(parameter.replace("?", ""), Integer.class);
|
||||
}
|
||||
@@ -1244,12 +1253,20 @@ public class ParameterBindingJsonReader extends AbstractBsonReader {
|
||||
} else {
|
||||
if (valueToken.getType() == JsonTokenType.INT32 || valueToken.getType() == JsonTokenType.INT64) {
|
||||
value = valueToken.getValue(Long.class);
|
||||
} else if (valueToken.getType() == JsonTokenType.STRING) {
|
||||
String dateTimeString = valueToken.getValue(String.class);
|
||||
try {
|
||||
value = DateTimeFormatter.parse(dateTimeString);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new JsonParseException("Failed to parse string as a date", e);
|
||||
} else if (valueToken.getType() == JsonTokenType.STRING
|
||||
|| valueToken.getType() == JsonTokenType.UNQUOTED_STRING) {
|
||||
|
||||
Object dt = bindableValueFor(valueToken).getValue();
|
||||
if (dt instanceof Date) {
|
||||
value = ((Date) dt).getTime();
|
||||
} else if (dt instanceof Number) {
|
||||
value = NumberUtils.convertNumberToTargetClass((Number) dt, Long.class);
|
||||
} else {
|
||||
try {
|
||||
value = DateTimeFormatter.parse(dt.toString());
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new JsonParseException(String.format("Failed to parse string '%s' as a date", dt), e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new JsonParseException("JSON reader expected an integer or string but found '%s'.",
|
||||
|
||||
@@ -20,6 +20,7 @@ import static org.assertj.core.api.Assertions.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.bson.Document;
|
||||
@@ -158,6 +159,42 @@ public class ParameterBindingJsonReaderUnitTests {
|
||||
assertThat(target).isEqualTo(new Document("arg0", 100.01D));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2315
|
||||
public void bindDateAsDate() {
|
||||
|
||||
Date date = new Date();
|
||||
Document target = parse("{ 'end_date' : { $gte : { $date : ?0 } } }", date);
|
||||
|
||||
assertThat(target).isEqualTo(Document.parse("{ 'end_date' : { $gte : { $date : " + date.getTime() + " } } } "));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2315
|
||||
public void bindQuotedDateAsDate() {
|
||||
|
||||
Date date = new Date();
|
||||
Document target = parse("{ 'end_date' : { $gte : { $date : '?0' } } }", date);
|
||||
|
||||
assertThat(target).isEqualTo(Document.parse("{ 'end_date' : { $gte : { $date : " + date.getTime() + " } } } "));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2315
|
||||
public void bindStringAsDate() {
|
||||
|
||||
Date date = new Date();
|
||||
Document target = parse("{ 'end_date' : { $gte : { $date : ?0 } } }", "2019-07-04T12:19:23.000Z");
|
||||
|
||||
assertThat(target).isEqualTo(Document.parse("{ 'end_date' : { $gte : { $date : '2019-07-04T12:19:23.000Z' } } } "));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2315
|
||||
public void bindNumberAsDate() {
|
||||
|
||||
Long time = new Date().getTime();
|
||||
Document target = parse("{ 'end_date' : { $gte : { $date : ?0 } } }", time);
|
||||
|
||||
assertThat(target).isEqualTo(Document.parse("{ 'end_date' : { $gte : { $date : " + time + " } } } "));
|
||||
}
|
||||
|
||||
private static Document parse(String json, Object... args) {
|
||||
|
||||
ParameterBindingJsonReader reader = new ParameterBindingJsonReader(json, args);
|
||||
|
||||
Reference in New Issue
Block a user