Search code examples
mongodbmongo-java-driver

Updating nested array document in MongoDB


So I have a mongo document like this and I need to update the array based on the val

{
    "_id" : NumberLong(222),
    "pattern":"grain"
    "BASIC" : {
        "frame":"clear"
        "tin" : [ 
            {
                "val" : "abc",
                "unit" : NumberLong(2311)
            }, 
            {
                "val" : "def",
                "unit" : NumberLong(2311)
            }, 
        ]
    }
}

Here is the code I've tried

collection = db.getCollection("test");
Bson where = new Document()
    .append("_id", 222)
    .append("BASIC.tin.val","abc");
Bson update = new Document()
    .append("BASIC.tin.$.val", "xyz");
Bson set = new Document()
    .append("$set", update);

try {
    UpdateResult result = collection.updateOne(where, set, new UpdateOptions().upsert(true));
                
    if (result.getMatchedCount() > 0){
        System.out.println("updated");
        System.out.println(result.getModifiedCount());
    } else {
        System.out.println("failed");
    }
} catch (MongoWriteException e) {
    e.printStackTrace();
}

The update works fine but does not upsert if the find fails This is the error which I get:

com.mongodb.MongoWriteException: The positional operator did not find the match needed from the query. Unexpanded update: BASIC.tin.$.val
at com.mongodb.MongoCollectionImpl.executeSingleWriteRequest(MongoCollectionImpl.java:558)
at com.mongodb.MongoCollectionImpl.update(MongoCollectionImpl.java:542)

Solution

  • The upsert into the embedded documents is not possible and hence the error. So to simulate upsert for embedded documents, you'll need to update your code like below. This checks for the modified count and if its 0 then it means we need to insert a new document in the embedded documents which makes use of push for this. This will do an upsert with "pqr" as val and "unit" as 400 for the example docs you have.

    Bson where = new Document().append("_id", 222).append("BASIC.tin.val","pqr");
    
    Bson update = new Document()
            .append("BASIC.tin.$.val", "xyz");
    Bson set = new Document().append("$set", update);
    
    try {
    
        UpdateResult result = collection.updateOne(where , set, new UpdateOptions());
    
        if(result.getModifiedCount() > 0){
            System.out.println("updated");
        } else if(result.getModifiedCount()==0){
              System.out.println("upserting");
              Bson where1 = new Document().append("_id", 222);
              Bson upsert = new Document().append("BASIC.tin", new Document().append("val", "pqr").append("unit", 400));;
              Bson push = new Document().append("$push", upsert);
              UpdateResult result1 = collection.updateOne(where1 , push, new UpdateOptions());
              if(result1.getModifiedCount() == 1)
                  System.out.println("upserted");
        }else {
            System.out.println("failed");
        }
    } catch (MongoWriteException e) {
      e.printStackTrace();
    }
    

    Sample Response after upsert

    {
        "_id": NumberLong(222),
        "pattern": "grain",
        "BASIC": {
            "frame": "clear",
            "tin": [{
                "val": "xyz",
                "unit": NumberLong(2311)
            }, {
                "val": "def",
                "unit": NumberLong(2311)
            }, {
                "val": "pqr",
                "unit": 400
            }]
        }
    }