1

I am trying to update a array field in a embedded document in mongodb using Spring Data MongoDB.

The structure of document which I want to be updated/inserted in mongodb collection is as below.

There can be more documents like this based on each type of the department , say "Sales","Marketing" and so on.

{
    "timestamp": "2014-09-26T04:00:00.000Z",
    "department": "accounts",
    "employee": [
        {
            "type": "regular",
            "names": [
                "Raj",
                "Kumar",
                "Shankar"
            ]
        },
        {
            "type": "contract",
            "names": [
                "Penny",
                "Sheldon",
                "bob"
            ]
        },
        {
            "type": "temp",
            "names": [
                "jerry",
                "kramer",
                "bubbleboy"
            ]
        }
    ]
}

Basically, I have my update query as below,

db.getCollection('mytest').update(
   { 
        "timestamp" : "2014-09-26T04:00:00.000Z",
        "department" : "accounts",
        "employee.type" : "regular"
    },
    { $addToSet: { "employee.$.names" : "Jo" } },
    {
        upsert: true
    }
)

I have added upsert: true because if there is no document matching the query I want to insert the document into the mytest collection.

When I execute the same from mongo shell , I am getting the below error.

   The positional operator did not find the match needed from the query. Unexpanded update: employee.$.names

Even If this works, I am not sure if we have similar support for implementing the same in Spring Data mongodb.

Also, my other query is, if I want to add/update employees for multiple departments, say "accounts" as well as "sales", it appears that I have to execute the same query with different values as the number of department I want to update ( if not present insert) accordingly.

Is there a better and efficient option like bulk/batch where-in I can update/insert employees for multiple departments at the same time in a single mongo update query. Also, is there support in spring data mongodb/mongotemplate for the same.

1
  • Did you get a chance to look at the below solution and see whether it resolves your problem? Commented Aug 15, 2016 at 8:33

1 Answer 1

1

Firstly, I should say that there are multiple scenarios that need to be handled as the document structure has embedded arrays. If possible, please try to redesign the document structure.

Short answer to error:- You get the position operator error if the query doesn't find the matching document.

That's why in my below solution, I have handled the scenario in catch block. If you can go through the unit test, you should be able to understand.

Solution:-

public Boolean updateDepartmentCollectionWithoutFind(String name, String timeStamp)
            throws JsonParseException, JsonMappingException, IOException {

        MongoOperations mongoOperations = getMongoConnection();

        Query query = new Query();
        query.addCriteria(Criteria.where("timestamp").is(timeStamp).and("department").is("accounts")
                .and("employee.type").is("regular"));
        Update update = new Update();
        update.addToSet("employee.$.names", name);

        try {

            System.out.println(query.toString());
            System.out.println(update.toString());

            WriteResult writeResult = mongoOperations.upsert(query, update, DepartmentCollection.class);

            if (writeResult != null) {

                System.out.println("111111111111111111 Update with position parameter has been successful :"
                        + writeResult.toString());

            }
        } catch (DataIntegrityViolationException die) {
            System.out.println("Update failed ====>" + die.getMessage());
            System.out.println("Trying to update without position parameters ...");

            Update updateWithoutPositionOperator = new Update();
            updateWithoutPositionOperator.addToSet("employee.names", name);
            WriteResult writeResultUpsert = mongoOperations.upsert(query, updateWithoutPositionOperator,
                    DepartmentCollection.class);

            if (writeResultUpsert != null) {

                System.out.println("2222222222222222222 Update without position parameter has been successful :"
                        + writeResultUpsert.toString());

            }

        }

        return true;

    }

My get connection method for reference:- You don't need this if you have the spring context and can change the above code based on your context.

@SuppressWarnings("resource")
public MongoOperations getMongoConnection() {

    return (MongoOperations) new AnnotationConfigApplicationContext(SpringMongoConfig.class)
            .getBean("mongoTemplate");
}

Unit Tests:- I am posting the unit test because you need to understand the scenario and solution. Please read the comment in each test and method name to understand which upsert gets executed on the each scenarios.

Test 3 is a special behavior. Please closely look at it.

@Test
    public void updateDepartmentCollectionWhenNoDocumentsPresent() throws JsonParseException, JsonMappingException, IOException {

        //Second upsert executed (i.e. without positional parameter)
        Assert.isTrue(queryOperations.updateDepartmentCollectionWithoutFind("Raj", "2014-09-26T04:00:00.000Z"));

    }

    @Test
    public void updateDCWhenMatchingDocumentsPresentButDocumentHasOnlyRegularEmployeeType() throws JsonParseException, JsonMappingException, IOException {

        //Second upsert executed  (i.e. without positional parameter)       
        Assert.isTrue(queryOperations.updateDepartmentCollectionWithoutFind("Jo", "2014-09-26T04:00:00.000Z"));

    }   

    @Test
    public void updateDCWhenMultipleTypesPresentButNoRegular() throws JsonParseException, JsonMappingException, IOException {

        //First upsert executed  (i.e. with positional parameter). However, it creates a new document.      
        Assert.isTrue(queryOperations.updateDepartmentCollectionWithoutFind("Jo", "2014-09-27T04:00:00.000Z"));

    }   

    @Test
    public void updateDCWhenMultipleTypesPresentIncludingRegular() throws JsonParseException, JsonMappingException, IOException {

        //First upsert executed (i.e. with positional parameter) and existing document has been updated.        
        Assert.isTrue(queryOperations.updateDepartmentCollectionWithoutFind("Jo", "2014-09-29T04:00:00.000Z"));

    }
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.