Closed
Description
Hello,
(probably) due to #4555 where QueryMapper.convertSimpleOrDocument
was refactored to use Stream collect, it's now not possible to update documents with arbitrary Map that has null values.
It can be replicated with following entity:
@Document
public class TestPojo {
@Id
private String id;
private Map<String, Object> data;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Map<String, Object> getData() {
return data;
}
public void setData(Map<String, Object> data) {
this.data = data;
}
and following test case:
public interface TestPojoRepository extends MongoRepository<TestPojo, String> {}
@Test
void testUpdateNullValue() {
var obj = new TestPojo();
obj.setData(Collections.singletonMap("test", "value"));
var saved = pojoRepository.save(obj);
var query = new Query().addCriteria(Criteria.where("id").is(saved.getId()));
var upd = new Update().set("data", Collections.singletonMap("test", null));
mongoTemplate.updateFirst(query, upd, TestPojo.class);
}
which yields following exception:
java.lang.NullPointerException
at java.base/java.util.Objects.requireNonNull(Objects.java:233)
at java.base/java.util.stream.Collectors.lambda$uniqKeysMapAccumulator$1(Collectors.java:180)
at java.base/java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
at java.base/java.util.Collections$2.tryAdvance(Collections.java:5073)
at java.base/java.util.Collections$2.forEachRemaining(Collections.java:5081)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682)
at org.springframework.data.mongodb.core.convert.QueryMapper.convertSimpleOrDocument(QueryMapper.java:620)
at org.springframework.data.mongodb.core.convert.QueryMapper.getMappedValue(QueryMapper.java:525)
at org.springframework.data.mongodb.core.convert.QueryMapper.getMappedObjectForField(QueryMapper.java:351)
at org.springframework.data.mongodb.core.convert.UpdateMapper.getMappedObjectForField(UpdateMapper.java:160)
at org.springframework.data.mongodb.core.convert.QueryMapper.getMappedObject(QueryMapper.java:170)
at org.springframework.data.mongodb.core.convert.UpdateMapper.getMappedObject(UpdateMapper.java:66)
at org.springframework.data.mongodb.core.convert.QueryMapper.convertSimpleOrDocument(QueryMapper.java:603)
at org.springframework.data.mongodb.core.convert.QueryMapper.getMappedKeyword(QueryMapper.java:410)
at org.springframework.data.mongodb.core.convert.QueryMapper.getMappedObject(QueryMapper.java:129)
at org.springframework.data.mongodb.core.convert.UpdateMapper.getMappedObject(UpdateMapper.java:66)
at org.springframework.data.mongodb.core.QueryOperations$UpdateContext.getMappedUpdate(QueryOperations.java:895)
at org.springframework.data.mongodb.core.MongoTemplate.doUpdate(MongoTemplate.java:1720)
at org.springframework.data.mongodb.core.MongoTemplate.updateFirst(MongoTemplate.java:1643)
at com.example.demo.DemoApplicationTests.testUpdateNullValue(DemoApplicationTests.java:36)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
Which is caused by this known "bug" within Stream implementation - https://bugs.openjdk.org/browse/JDK-8148463
Is there any plan to either revert the refactor to the previous implementation logic or rewrite it from scratch in a way where it will support null values? I'm willing to create PR + write relevant test cases if I get suggestion what would be the suggested fix.
Thanks