Search code examples
mongodbspring-bootserialization

Converting an epoch to Date with MongoDB and SpringBoot


I have a large collection of documents in my db, which has not been maintained very well. Most of the time the createdOn is stored as "yyyy-dd-MM HH:mm:ssZ" pattern, but there's several records in which this same field is somehow stored using an epoch format.

Below are samples extracted using MongoDB Compass as Default Extended JSON

Document with no issue

"createdOn": {
    "$date": "2019-05-08T07:41:23.312Z"
  }

Document with issues

  "createdOn": {
    "$numberLong": "1678864933943"
  }

When querying the documents, Spring (or Jackson, or the Java Mongo driver) retrieves this value as a Double, then attempts to convert it back to my entity which is a Date object.

@Data
public class BaseModel {
    @Id
    @JsonSerialize(using= ObjectIdSerializer.class)
    protected ObjectId _id;
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-dd-MM HH:mm:ssZ")
    @JsonProperty("createdAt")
    Date createdOn;

This results in the following error being thrown

org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.Double] to type [java.util.Date]
    at org.springframework.core.convert.support.GenericConversionService.handleConverterNotFound(GenericConversionService.java:321)
    at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:194)
    at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:174)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.getPotentiallyConvertedSimpleRead(MappingMongoConverter.java:1032)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.readValue(MappingMongoConverter.java:1584)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter$MongoDbPropertyValueProvider.getPropertyValue(MappingMongoConverter.java:1478)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.readProperties(MappingMongoConverter.java:450)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.populateProperties(MappingMongoConverter.java:367)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:347)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:317)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:250)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:246)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:98)
    at org.springframework.data.mongodb.core.MongoTemplate$ReadDocumentCallback.doWith(MongoTemplate.java:3141)
    at org.springframework.data.mongodb.core.MongoTemplate.executeFindMultiInternal(MongoTemplate.java:2788)
    at org.springframework.data.mongodb.core.MongoTemplate.doFind(MongoTemplate.java:2518)
    at org.springframework.data.mongodb.core.MongoTemplate.doFind(MongoTemplate.java:2500)
    at org.springframework.data.mongodb.core.MongoTemplate.find(MongoTemplate.java:856)
    at org.springframework.data.mongodb.core.MongoTemplate.find(MongoTemplate.java:842)

... <omitted further lines, will post if requested>

Is there a simple solution that will cater for the 2 (or more) possible date formats that is stored in the db to be retrieved gracefully?

I've tried registering a converter to Spring (using @Component with a class implementing Converter<Double, Date>) but that didn't work.

I've also tried using @JsonDeserialize(using= DateDeserializers.DateDeserializer.class) to no avail either.

Limitations :

  1. I would prefer to not modify the Entity data types, so no changing to LocalDate or Instantas I am concerned of potential side effects as this project is rather huge and the modification for this field is on the BaseModel which is the base entity that all other entities extends from.
  2. I can't fix the documents. There's way too many and not feasible to patch the data.
  3. I can add dependencies if it's required to fix the problem, but updating or removing will also potentially have unwanted side effects.

Additional information (versions and stuff):

  1. Spring Boot -> v2.3.6.RELEASE
  2. javers-spring-boot-starter-mongo -> v5.10.0
  3. mongock-spring-v5 -> v4.3.7
  4. mongodb-springdata-v3-driver -> v4.3.7

Footnote : I realised the question title may be misleading but I really have no idea how else to describe this problem in a single sentence.


Solution

  • The issue is that the converter required is on the Mongo repository side. To solve the issue, I had to register a converter to the MongoCustomConversions

    @ReadingConverter
    public class MyCustomConverter implements Converter<Double, Date> {
    
        @Override
        public Date convert(Double source) {
            //handle here
            return handledValue;
        }
    
    }
    
    
    @Configuration
    public class ConverterConfig {
        @Bean
        public MongoCustomConversions mongoCustomConversions() {
            List<Converter> list = new ArrayList<>();
            list.add(new MyCustomConverter());
            return new MongoCustomConversions(list);
        }
    }
    

    Reference StackOverflow Post