8

Want to understand how to perform an upsert operation in an array for a given document in MongoDB.

I have the following json document

  {
        "firstName": "John",
        "lastName": "Paul",
        "contact": {
            "contactGroup":"Business",
            "myContacts": [{
                    "name": "Jeff",
                    "phone": "222 - 572 - 8754"
                },
                {
                    "name": "Joe",
                    "phone": "456 - 875 - 4521"
                }
            ]
        }
    }

I want to perform the upsert operation at the following levels:

  1. firstName
  2. myContacts array

Below is the code snippet that I have worked on. currently, I am using MongoDB's addtoSet operator for myContacts but the behavior seems to perform only adds a value to an array unless the value is already present.

Person class:

@Document
public class Person{

    @Id
    private String id;
    private String firstName;
    private String lastName;
    private Contact contact;
    //Setter and Getter methods
}

Contact class:

public class Contact{
    private String contactGroup;
    private List<MyContacts> myContacts;

    //Setter & Getter methods
}

MyContacts class:

public class MyContacts{

    private String contactName;
    private String contactPhone;

    //Setter and Getter methods
}

ContactsUpdate:

public class ContacsUpdate {

@Autowired
private MongoOperations mongoOps;


    // This method receives list of person objects
    public void upsertMongoContact(List<Person> persons) {

        for (Person person : persons) {

            Update updateCmd = new Update();
            Query query = new Query();
            query.addCriteria((Criteria.where("firstName").is((person.firstName()))));

            for (MyContacts contact : person.getContact().getmyContacts()) {
                updateCmd.addToSet("contact.myContacts.", contact);
            }
            mongoOps.findAndModify(query, updateCmd, FindAndModifyOptions.options().upsert(true), Person.class);
        }
    }

}

Is there any way of updating mycontacts array based on the name. If not perform an insert operation.

2
  • what problems you experiencing? Commented Jan 2, 2018 at 15:37
  • @Remario: In case if I change any of the phone number for the existing contacts in my input data. A new element is added in the array instead of updating the existing contact Commented Jan 2, 2018 at 15:40

2 Answers 2

5

Upsert into array is not possible.

So to simulate upsert for array for a happy path scenario, you'll have to do it in 2 different updates.

The code below will look for array element with matching name, when found it will update the matched element with newer phone number; if not found it will insert a new array element with name and phone number.

In essence we first try to locate array element with matching name and try to set the phone value when it succeeds the modified count should be greater than 0. If it zero we have to insert a new document in the array which makes use of push for this. This will do an upsert with new array element.

Something like in Spring Mongo 2.0.2 version

 public void upsertMongoContact(List<Person> persons) {

    for (Person person : persons) {
        for (MyContacts contact : person.getContact().getmyContacts()) {
            Query sQuery = new Query();
            Criteria sCriteria = Criteria.where("firstName").is((person.firstName()));
            sCriteria.and("contact.myContacts.name").is(contact.name());
            sQuery.addCriteria(sCriteria);
            Update sUpdate = new Update();
            sUpdate.set("contact.myContacts.$.phone", person.phone());
            UpdateResult sUpdateResult = mongoOps.updateFirst(sQuery, sUpdate, Person.class);
            if (sUpdateResult.getModifiedCount() == 0) {
                Query pQuery = new Query();
                Criteria pCriteria = Criteria.where("firstName").is((person.firstName()));
                pQuery.addCriteria(pCriteria);
                Update pUpdate = new Update();
                pUpdate.push("contact.myContacts", contact);
                mongoOps.updateFirst(pQuery, pUpdate, Person.class);
            }
        }
    }
}

Reference : Updating nested array document in MongoDB

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

5 Comments

Thank you this works!! What could be the negative scenarios in this case?
Np. Modified count is pretty stable indicator whether the write went through or not. We are not doing atomic updates which will cause you to insert duplicates. You can use addToSet instead of push in the second update to account for duplicates. For any other unexpected modified count you can log them appropriately
yes I have updated my code to addToSet to avoid duplicates
Since 2nd loops iterate over MyContact Lists, so how does it work if the list is empty?
Can You Please Clarify The Usage Of '$' In The update.set() Method?
0

I believe the problem is in the addSet part. Try this. Remove the for loop for addToSet. Replace with below

updateCmd.addToSet("myContacts", person)

Hope this helps.

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.