mongo-java-driver update each item of array field belonging to document applying some conditional logic

55 Views Asked by At

I'm working on an event collection, where each document is modeled like so:

{
"event_name": "John Doe Concert",
"event_date": "2022-01-01"
"ticket_types": [
    {
      "name": "Front seats",
      "total": 50 (we'll call this `oldTotal1`),
      "available": 25 (we'll call this `oldAvailable1`)
    },
    {
      "name": "Back seats",
      "total": 100 (we'll call this `oldTotal2`),
      "available": 50 (we'll call this `oldAvailable2`)
    }
]
}

Suppose I have a REST API supporting a PUT endpoint which accepts a payload like this:

{
"event_name": "Jane Doe Concert",
"event_date": "2022-02-02"
"ticket_types": [
    {
      "name": "Front seats",
      "total": 100 (we'll call this `newTotal1`),
    },
    {
      "name": "Back seats",
      "total": 150 (we'll call this `newTotal2`),
    }
]
}

As you can see, I'm looking to update the document (doesn't matter if it's update or replace as long as the operation is atomic on a document level). For each element i in the ticket_types array in the payload, we can assume newTotal[i] >= oldTotal[i]

The way I'd like my document to look like after the update:

{
"event_name": "Jane Doe Concert",
"event_date": "2022-02-02"
"ticket_types": [
    {
      "name": "Front seats",
      "total": 100 (the value of `newTotal1`),
      "available": 75 (the value of `oldAvailable1` + `newTotal1` - `oldTotal1`)
    },
    {
      "name": "Back seats",
      "total": 150 (the value of `newTotal2`),
      "available": 100 (the value of `oldAvailable2` + `newTotal2` - `oldTotal2`)
    }
]
}

The problem I'm having is that I would like to perform the calculations of the resulting values of total and available exclusively through Mongo's findAndUpdate operation (without fetching the document first and doing the changes via Java code after so to speak). This is because the environment the code is run in is AWS Lambda, so I don't think I can rely on any locking mechanism other than the DB's own mechanisms, nor would I want to touch MongoDB's Transaction API.

My attempt so far:

final Document result = collection.findOneAndUpdate(query, Updates.combine(
    Updates.set("event_name", request.getEventName()),
    Updates.set("event_date", request.getDate().toString()),
   // please help me!
    Updates.set("ticket_types.$[elem].total", ...)
    Updates.set("ticket_types.$[elem].available", ...)

Javascript equivalent should be:

db.getCollection("events").updateOne({name: 'John Doe Concert'},
 {
     $set: {
         "ticket_types.$[first].total": 1000,
         "ticket_types.$[first].available":  "ticket_types.$[first].available" + 1000 - "ticket_types.$[first].total",
          "ticket_types.$[second].total": 2000,
         "ticket_types.$[second].available":  "ticket_types.$[second].available" + 2000 - "ticket_types.$[second].total" 
     }
    
 },
{ 
    arrayFilters: [  
                 { "first.name": "Front seats" },
                 { "second.name": "Back seats" }     
    ]
}
)

Edit: The goal is to update the data doing calculation using existing attributes. I feel it's possible to achieve this using an update aggregation pipeline.

I am using DocumentDB 4.0 with application layer written in Java

Thanks

0

There are 0 best solutions below