spring-data-mongodb icon indicating copy to clipboard operation
spring-data-mongodb copied to clipboard

Update with $unset fails with mapping exception

Open csmager opened this issue 2 years ago • 7 comments

I'm using spring-data-mongodb 4.0.8. On creating an update with an $unset, the following is added to the document:

{  $unset: { fieldName: 1 } }

The value here - 1 - is then attempted to be mapped to the correct type for that field. In my case, that's Decimal128. I get this stack trace:

org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.Integer] to type [org.bson.types.Decimal128] for value [1]
 at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:47)
 at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:192)
 at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:175)
 at org.springframework.data.mongodb.core.convert.QueryMapper.applyFieldTargetTypeHintToValue(QueryMapper.java:848)
 at org.springframework.data.mongodb.core.convert.QueryMapper.getMappedValue(QueryMapper.java:436)
 at org.springframework.data.mongodb.core.convert.QueryMapper.getMappedObjectForField(QueryMapper.java:339)
 at org.springframework.data.mongodb.core.convert.UpdateMapper.getMappedObjectForField(UpdateMapper.java:160)
 at org.springframework.data.mongodb.core.convert.QueryMapper.getMappedObject(QueryMapper.java:166)
 at org.springframework.data.mongodb.core.convert.UpdateMapper.getMappedObject(UpdateMapper.java:66)
 at org.springframework.data.mongodb.core.convert.QueryMapper.convertSimpleOrDocument(QueryMapper.java:578)
 at org.springframework.data.mongodb.core.convert.QueryMapper.getMappedKeyword(QueryMapper.java:399)
 at org.springframework.data.mongodb.core.convert.QueryMapper.getMappedObject(QueryMapper.java:146)
 at org.springframework.data.mongodb.core.convert.UpdateMapper.getMappedObject(UpdateMapper.java:66)
 at org.springframework.data.mongodb.core.QueryOperations$UpdateContext.getMappedUpdate(QueryOperations.java:864)
 at org.springframework.data.mongodb.core.MongoTemplate.doUpdate(MongoTemplate.java:1617)
 at org.springframework.data.mongodb.core.MongoTemplate.updateFirst(MongoTemplate.java:1540)

The value here does not relate to the field value, it should not be mapped. My current workaround is to override unset as below:

var update = new Update() {
    @Override
    public Update unset(String key) {
        addMultiFieldOperation("$unset", key, "");
        return this;
    }
};

The mapping does not fail with an empty string, and an empty string is what's used in the examples in the docs.

csmager avatar Nov 20 '23 11:11 csmager

@csmager thanks for reporting! We'll see what we can do about this.

christophstrobl avatar Nov 20 '23 11:11 christophstrobl

@csmager I might be missing something as I was not able to reproduce the issue on 4.0.x nor current main. Could you please take the time to provide a complete minimal sample (something that we can unzip or git clone, build, and deploy) that reproduces the problem.

christophstrobl avatar Nov 21 '23 11:11 christophstrobl

Of course - I'd been a little lazy and hoped it'd be easily reproducible. I'll try and reproduce.

csmager avatar Nov 21 '23 11:11 csmager

The repo is here: https://github.com/csmager/spring-data-mongodb-repro

But I suspect all you need is this. It appears the field mapping to Decimal128 is important - I tried the default (which I think is string), and I tried other types that I thought ought to fail (like ObjectId or Date), and none throw.

@SpringBootTest
class SpringDataMongoDbReproTest {

    @Autowired
    private MongoTemplate mongoTemplate;

    @Test
    void throws_on_unset() {
        this.mongoTemplate.updateMulti(
            new Query(),
            new Update()
                .unset("field"),
            Entity.class
        );
    }

    public static class Entity {
        private ObjectId id;
        @Field(targetType = FieldType.DECIMAL128)
        private BigDecimal field;
    }
}

csmager avatar Nov 21 '23 15:11 csmager

ah thanks - I was using Decimal128 as field type directly.

christophstrobl avatar Nov 23 '23 12:11 christophstrobl

Yeah, it does seem it's the type hint / mapping that's causing it, it's probably quite a niche issue.

csmager avatar Nov 23 '23 13:11 csmager

Unfortunately there's no easy fix for the issue as we'd need to provide more context to the update mapping (which needs a bit more thought) to decide if conversion needs to be applied.

christophstrobl avatar Dec 07 '23 07:12 christophstrobl