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 {
|
static {
|
||||||
FormatterImpl dateTimeHelper;
|
FormatterImpl dateTimeHelper;
|
||||||
try {
|
try {
|
||||||
dateTimeHelper = loadDateTimeFormatter("org.bson.json.DateTimeFormatter$Java8DateTimeFormatter");
|
dateTimeHelper = loadDateTimeFormatter(
|
||||||
|
"org.springframework.data.mongodb.util.json.DateTimeFormatter$Java8DateTimeFormatter");
|
||||||
} catch (LinkageError e) {
|
} catch (LinkageError e) {
|
||||||
// this is expected if running on a release prior to Java 8: fallback to JAXB.
|
// 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;
|
FORMATTER_IMPL = dateTimeHelper;
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import static org.bson.codecs.configuration.CodecRegistries.*;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@@ -35,22 +36,13 @@ import org.bson.BsonValue;
|
|||||||
import org.bson.BsonWriter;
|
import org.bson.BsonWriter;
|
||||||
import org.bson.Document;
|
import org.bson.Document;
|
||||||
import org.bson.Transformer;
|
import org.bson.Transformer;
|
||||||
import org.bson.codecs.BsonTypeClassMap;
|
import org.bson.codecs.*;
|
||||||
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.configuration.CodecRegistry;
|
import org.bson.codecs.configuration.CodecRegistry;
|
||||||
|
|
||||||
import org.springframework.data.spel.EvaluationContextProvider;
|
import org.springframework.data.spel.EvaluationContextProvider;
|
||||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
|
import org.springframework.util.NumberUtils;
|
||||||
|
import org.springframework.util.ObjectUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -278,6 +270,17 @@ public class ParameterBindingDocumentCodec implements CollectibleCodec<Document>
|
|||||||
if (bindingReader.currentValue != null) {
|
if (bindingReader.currentValue != null) {
|
||||||
|
|
||||||
Object value = bindingReader.currentValue;
|
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.setState(State.TYPE);
|
||||||
bindingReader.currentValue = null;
|
bindingReader.currentValue = null;
|
||||||
return value;
|
return value;
|
||||||
|
|||||||
@@ -394,7 +394,7 @@ public class ParameterBindingJsonReader extends AbstractBsonReader {
|
|||||||
matched = true;
|
matched = true;
|
||||||
String group = matcher.group();
|
String group = matcher.group();
|
||||||
int index = computeParameterIndex(group);
|
int index = computeParameterIndex(group);
|
||||||
computedValue = computedValue.replace(group, ObjectUtils.nullSafeToString(getBindableValueForIndex(index)));
|
computedValue = computedValue.replace(group, nullSaveToString(getBindableValueForIndex(index)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!matched) {
|
if (!matched) {
|
||||||
@@ -406,7 +406,7 @@ public class ParameterBindingJsonReader extends AbstractBsonReader {
|
|||||||
String binding = regexMatcher.group();
|
String binding = regexMatcher.group();
|
||||||
String expression = binding.substring(3, binding.length() - 1);
|
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;
|
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) {
|
private static int computeParameterIndex(String parameter) {
|
||||||
return NumberUtils.parseNumber(parameter.replace("?", ""), Integer.class);
|
return NumberUtils.parseNumber(parameter.replace("?", ""), Integer.class);
|
||||||
}
|
}
|
||||||
@@ -1244,12 +1253,20 @@ public class ParameterBindingJsonReader extends AbstractBsonReader {
|
|||||||
} else {
|
} else {
|
||||||
if (valueToken.getType() == JsonTokenType.INT32 || valueToken.getType() == JsonTokenType.INT64) {
|
if (valueToken.getType() == JsonTokenType.INT32 || valueToken.getType() == JsonTokenType.INT64) {
|
||||||
value = valueToken.getValue(Long.class);
|
value = valueToken.getValue(Long.class);
|
||||||
} else if (valueToken.getType() == JsonTokenType.STRING) {
|
} else if (valueToken.getType() == JsonTokenType.STRING
|
||||||
String dateTimeString = valueToken.getValue(String.class);
|
|| valueToken.getType() == JsonTokenType.UNQUOTED_STRING) {
|
||||||
try {
|
|
||||||
value = DateTimeFormatter.parse(dateTimeString);
|
Object dt = bindableValueFor(valueToken).getValue();
|
||||||
} catch (IllegalArgumentException e) {
|
if (dt instanceof Date) {
|
||||||
throw new JsonParseException("Failed to parse string as a date", e);
|
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 {
|
} else {
|
||||||
throw new JsonParseException("JSON reader expected an integer or string but found '%s'.",
|
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.nio.charset.StandardCharsets;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.bson.Document;
|
import org.bson.Document;
|
||||||
@@ -158,6 +159,42 @@ public class ParameterBindingJsonReaderUnitTests {
|
|||||||
assertThat(target).isEqualTo(new Document("arg0", 100.01D));
|
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) {
|
private static Document parse(String json, Object... args) {
|
||||||
|
|
||||||
ParameterBindingJsonReader reader = new ParameterBindingJsonReader(json, args);
|
ParameterBindingJsonReader reader = new ParameterBindingJsonReader(json, args);
|
||||||
|
|||||||
Reference in New Issue
Block a user