Mark Bramnik
Mark Bramnik

Reputation: 42441

MongoDB - Update parts of object

I have the collection that stores documents per some execution Flow. Every Process includes "processes" and each process includes steps. So I end up with a 'flows' collection that has documents that look like this:

{
   "name" : "flow1",
   "description" : "flow 1 description",
   "processes" : [
    {
      "processId" : "firstProcessId",
      "name" : "firstProcessName",
      "startedAt" : null,
      "finishedAt" : null,
      "status" : "PENDING", 
       "steps" : [
        {
         "stepId" : "foo",               ​
         ​"status" : "PENDING",
         ​"startedAt" : null, 
         ​"finishedAt" : null 
        },
        {
         "stepId" : "bar",​
         ​"status" : "PENDING",
         ​"startedAt" : null, 
         ​"finishedAt" : null 
        }
        ...
      ​]
    },
    {
      "processId" : "secondProcessId",
      "name" : "secondProcessName",
      "startedAt" : null,
      "finishedAt" : null,
      "status" : "PENDING", 
       "steps" : [
        {
         "stepId" : "foo",               ​
         ​"status" : "PENDING",
         ​"startedAt" : null, 
         ​"finishedAt" : null 
        },
        {
         "stepId" : "xyz",​
         ​"status" : "PENDING",
         ​"startedAt" : null, 
         ​"finishedAt" : null 
        }
        ...
      ​]
    }
}

A couple of notes here: Each flow contains many processes Each process contains at least one step, it is possible that in different processes the steps with the same id might appear (id is something that the programmer specifies),

It can be something like "step of bringing me something from the DB", so this is a kind of reusable component in my system.

Now, when the application runs I would like to call DAO's method like "startProcess", "startStep".

So I would like to know what is the correct query for starting step given processId and steps.

I can successfully update the process description to "running" given the flow Id and the process Id:

db.getCollection('flows').updateOne({"name" : "flow1", "processes" : {$elemMatch : {"processId" : "firstProcessId"}}}, {$set: {"processes.$.status" : "RUNNING"}})

However I don't know how to update the step status given the flowId, process Id and step Id, it looks like it doesn't allow multiple "$" signs in the path:

So, this doesn't work:

db.getCollection('flows').updateOne({"name" : "flow1", "processes" : {$elemMatch : {"processId" : "firstProcessId"}}, "processes.steps.stepId" : {$elemMatch : {"stepId" : "foo"}}}, {$set: {"processes.$.steps.$.status" : "RUNNING"}})

What is the best way to implement such an update?

Upvotes: 0

Views: 88

Answers (2)

Tom Slabbaert
Tom Slabbaert

Reputation: 22276

As you mentioned it does not work with multiple arrays, straight from the docs:

The positional $ operator cannot be used for queries which traverse more than one array, such as queries that traverse arrays nested within other arrays, because the replacement for the $ placeholder is a single value

I recommend you use arrayFilters instead, it's behavior is much clearer especially when working with nested structures:

db.collection.updateMany(
{
  "name": "flow1",
  "processes.processId": "firstProcessId",
  "processes.steps.stepId": "foo"
},
{
  $set: {
    "processes.$[process].steps.$[step].status": "RUNNING"
  }
},
{
  arrayFilters: [
    {
      "process.processId": "firstProcessId"
    },
    {
      "step.stepId": "foo"
    }
  ]
})

Mongo Playground

Upvotes: 3

Yong Shun
Yong Shun

Reputation: 51125

To update the document in multi-level nested array, you need $[<identifier>] filtered positional operator and arrayFilters.

And the processes and processes.steps.stepId filter in the match operator can be removed as the filter is performed in arrayFilters.

db.collection.update({
  "name": "flow1"
},
{
  $set: {
    "processes.$[process].steps.$[step].status": "RUNNING"
  }
},
{
  arrayFilters: [
    {
      "process.processId": "firstProcessId"
    },
    {
      "step.stepId": "foo"
    }
  ]
})

Sample Mongo Playground


Reference

Update Nested Arrays in Conjunction with $[]

Upvotes: 3

Related Questions