Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set value of Map with numeric key breaks data structure #3921

Closed
derekxia1988 opened this issue Dec 30, 2021 · 2 comments
Closed

Set value of Map with numeric key breaks data structure #3921

derekxia1988 opened this issue Dec 30, 2021 · 2 comments
Assignees
Labels
type: regression A regression from a previous release

Comments

@derekxia1988
Copy link

Version with problems:

SpringBoot 2.6.2, 2.5.4 or above, 2.3.10.RELEASE or above
SpringDataMongoDB 3.3.0 3.2.4 or above, 3.0.8.RELEASE or above

these are the versions I test, maybe not very accurate, but the infomation may make some effect

Brief introduction

Just as the title says, MongoTemplate.UpdateFirst just went wrong and break the document structure

How to reproduce

I just past my code, the test method and data classes in one class:

package com.example.mongotest.test;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

import javax.annotation.PostConstruct;

@Component
public class Tester {

    private final MongoTemplate mongoTemplate;

    public Tester(MongoTemplate mongoTemplate) {
        this.mongoTemplate = mongoTemplate;
    }

    @PostConstruct
    public void test() {
        TestValue testValue = new TestValue();
        testValue.setIntValue(1);

        Map<Integer, TestValue> testMap = new HashMap<>(16);
        testMap.put(1, testValue);

        TestInnerData testInnerData = new TestInnerData();
        testInnerData.setTestMap(testMap);

        TestData testData = new TestData();
        testData.setId("2");
        testData.setTestInnerData(testInnerData);

        mongoTemplate.save(testData);

        mongoTemplate.updateFirst(
                Query.query(Criteria.where("_id").is("2")),
                Update.update("testInnerData.testMap.1.intValue", 2),
                TestData.class
        );
        mongoTemplate.updateFirst(
                Query.query(Criteria.where("_id").is("2")),
                Update.update("testInnerData.testMap.1.intValue", 3),
                TestData.class
        );
    }

    @Document
    private static class TestData {
        @Id
        private String id;
        private TestInnerData testInnerData;

        public String getId() {
            return id;
        }

        public void setId(String id) {
            this.id = id;
        }

        public TestInnerData getTestInnerData() {
            return testInnerData;
        }

        public void setTestInnerData(TestInnerData testInnerData) {
            this.testInnerData = testInnerData;
        }
    }

    private static class TestInnerData {
        private Map<Integer, TestValue> testMap;

        public Map<Integer, TestValue> getTestMap() {
            return testMap;
        }

        public void setTestMap(Map<Integer, TestValue> testMap) {
            this.testMap = testMap;
        }
    }

    private static class TestValue {
        private int intValue;

        public int getIntValue() {
            return intValue;
        }

        public void setIntValue(int intValue) {
            this.intValue = intValue;
        }
    }
}

The result in MongoDB with SpringBoot2.6.2(or 2.5.X, X), SpringDataMongoDB3.3.0:


  {
    _id: '2',
    testInnerData: { testMap: { '1': { intValue: 1 }, intValue: { intValue: 3 } } },
    _class: 'com.example.mongotest.test.Tester$TestData'
  }

Change SpringBoot version to 2.3.9.RELEASE(SpringDataMongoDB 3.0.7.RELEASE), we get the expected result:


  {
    _id: '2',
    testInnerData: { testMap: { '1': { intValue: 3 } } },
    _class: 'com.example.mongotest.test.Tester$TestData'
  }

As the result shows, we get wrong result with springboot2.6.2, I don't know this has some relation with #3689 or #3775 , in issue #3689 similar numeric key bug happens, but marked as resolved and backported to Spring Data MongoDB3.2.X. So with the infomation above, hope you can figure out what goes wrong, if this is not a bug but my mistake in using SpringDataMongoDB, I'm sorry for my interrupt.

@derekxia1988
Copy link
Author

derekxia1988 commented Dec 30, 2021

More infomation:
I tested springboot2.5.3 with springdatamongodb3.2.3, the problem still exists, but in springboot2.5.2 with springdatamongodb3.2.2 the problem disappears, I got some other kind of structure bugs with springboot2.5.2.

@derekxia1988
Copy link
Author

derekxia1988 commented Dec 30, 2021

I extend my test code to reproduce the bug is springboot2.5.2:

package com.example.mongotest.test;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

import javax.annotation.PostConstruct;

@Component
public class Tester {

    private final MongoTemplate mongoTemplate;

    public Tester(MongoTemplate mongoTemplate) {
        this.mongoTemplate = mongoTemplate;
    }

    @PostConstruct
    public void test() {
        TestValue2 testValue2 = new TestValue2();
        testValue2.setIntValue2(2);

        Map<Integer, TestValue2> testMap2 = new HashMap<>(16);
        testMap2.put(2, testValue2);

        TestValue testValue = new TestValue();
        testValue.setIntValue(1);
        testValue.setTestMap2(testMap2);

        Map<Integer, TestValue> testMap = new HashMap<>(16);
        testMap.put(1, testValue);

        TestInnerData testInnerData = new TestInnerData();
        testInnerData.setTestMap(testMap);

        TestData testData = new TestData();
        testData.setId("1");
        testData.setTestInnerData(testInnerData);

        mongoTemplate.save(testData);

        mongoTemplate.updateFirst(
                Query.query(Criteria.where("_id").is("1")),
                Update.update("testInnerData.testMap.1.intValue", 2),
                TestData.class
        );
        mongoTemplate.updateFirst(
                Query.query(Criteria.where("_id").is("1")),
                Update.update("testInnerData.testMap.1.intValue", 3),
                TestData.class
        );
        mongoTemplate.updateFirst(
                Query.query(Criteria.where("_id").is("1")),
                Update.update("testInnerData.testMap.1.testMap2.2.intValue2", 3),
                TestData.class
        );
        mongoTemplate.updateFirst(
                Query.query(Criteria.where("_id").is("1")),
                Update.update("testInnerData.testMap.1.testMap2.2.intValue2", 4),
                TestData.class
        );
    }

    @Document
    private static class TestData {
        @Id
        private String id;
        private TestInnerData testInnerData;

        public String getId() {
            return id;
        }

        public void setId(String id) {
            this.id = id;
        }

        public TestInnerData getTestInnerData() {
            return testInnerData;
        }

        public void setTestInnerData(TestInnerData testInnerData) {
            this.testInnerData = testInnerData;
        }
    }

    private static class TestInnerData {
        private Map<Integer, TestValue> testMap;

        public Map<Integer, TestValue> getTestMap() {
            return testMap;
        }

        public void setTestMap(Map<Integer, TestValue> testMap) {
            this.testMap = testMap;
        }
    }

    private static class TestValue {
        private int intValue;
        private Map<Integer, TestValue2> testMap2;

        public int getIntValue() {
            return intValue;
        }

        public void setIntValue(int intValue) {
            this.intValue = intValue;
        }

        public Map<Integer, TestValue2> getTestMap2() {
            return testMap2;
        }

        public void setTestMap2(Map<Integer, TestValue2> testMap2) {
            this.testMap2 = testMap2;
        }
    }

    private static class TestValue2 {
        private int intValue2;

        public int getIntValue2() {
            return intValue2;
        }

        public void setIntValue2(int intValue2) {
            this.intValue2 = intValue2;
        }
    }
}

the result with springboot2.5.2:


  {
    _id: '1',
    testInnerData: {
      testMap: {
        '1': {
          intValue: 3,
          testMap2: { '2': { intValue2: 2 }, testMap2: { intValue2: 4 } }
        }
      }
    },
    _class: 'com.example.mongotest.test.Tester$TestData'
  }

the result with springboot 2.6.2:


  {
    _id: '1',
    testInnerData: {
      testMap: {
        '1': { intValue: 1, testMap2: { '2': { intValue2: 2 } } },
        intValue: { intValue: 3 },
        testMap2: { testMap2: { '2': { intValue2: 4 } } }
      }
    },
    _class: 'com.example.mongotest.test.Tester$TestData'
  }

the expected result with springboot 2.3.9.RELEASE:


  {
    _id: '1',
    testInnerData: {
      testMap: {
        '1': { intValue: 3, testMap2: { '2': { intValue2: 4 } } }
      }
    },
    _class: 'com.example.mongotest.test.Tester$TestData'
  }

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Jan 4, 2022
@christophstrobl christophstrobl self-assigned this Jan 10, 2022
@christophstrobl christophstrobl added type: regression A regression from a previous release and removed status: waiting-for-triage An issue we've not yet triaged labels Jan 11, 2022
mp911de pushed a commit that referenced this issue Jan 13, 2022
This commit removes usage of the iterator and replaces map key and positional parameter mappings with an index based token lookup.

Closes #3921
Original pull request: #3930.
mp911de added a commit that referenced this issue Jan 13, 2022
Simplify assertions, reformat code.

See #3921
Original pull request: #3930.
mp911de added a commit that referenced this issue Jan 13, 2022
Simplify assertions, reformat code.

See #3921
Original pull request: #3930.
mp911de pushed a commit that referenced this issue Jan 13, 2022
This commit removes usage of the iterator and replaces map key and positional parameter mappings with an index based token lookup.

Closes #3921
Original pull request: #3930.
mp911de added a commit that referenced this issue Jan 13, 2022
Simplify assertions, reformat code.

See #3921
Original pull request: #3930.
@mp911de mp911de added this to the 3.2.8 (2021.0.8) milestone Jan 13, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: regression A regression from a previous release
Projects
None yet
4 participants