DATAMONGO-2315 - Fix $date parameter binding for string based queries.

Original pull request: #772.
This commit is contained in:
Christoph Strobl
2019-07-04 14:09:12 +02:00
committed by Mark Paluch
parent 296e97903d
commit 8ca16e1cb4
4 changed files with 81 additions and 22 deletions

View File

@@ -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;

View File

@@ -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;

View File

@@ -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'.",

View File

@@ -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);