$push into array while $set'ing fields of existing elements

I’m trying to insert documents into an array while also updating fields of existing elements in the same array. Am I doing something wrong, or is this not possible in a single operation?

I’m seeing this error: Updating the path 'userJourneyEvents.$[event].touchpoints' would create a conflict at 'userJourneyEvents.$[event].touchpoints'

I’m using the NodeJS driver, calling findOneAndUpdate with the following parameters:

query {
  userJourneyEvents: { 
    '$elemMatch': { id: 'eid' }
  }
}
eventUpdate {
  '$set': {
    'userJourneyEvents.$[event].touchpoints.$[touchpoint0].description': 'description'
  },
  '$push': {
    'userJourneyEvents.$[event].touchpoints': {
      '$each': [
        {
          id: 'new_id',
          kind: 'tools',
          description: ''
        }
      ],
      '$sort': { kind: 1 }
    }
  }
}
arrayFilters [
  { 'event.id': 'eid' },
  { 'touchpoint0.id': 'tpid0' }
]

Hi @Jon_Madden,

Welcome to MongoDB community.

I would like to o help you but I believe the best way is to first :

  1. Please provide a sample document to understand the structure.
  2. The desired end state of the doc.

Thanks,
Pavel

Hi @Pavel_Duchovny! Thanks for the response. Here’s some sample documents:

Before

{
 "userJourneyEvents": [
   {
     "id": "eid",
     "touchpoints": [
       {
         "id": "tpid0",
         "kind": "tools",
         "description": ""
       }
     ]
   }
 ]
}

After

{
  "userJourneyEvents": [
    {
      "id": "eid",
      "touchpoints": [
        {
          "id": "tpid0",
          "kind": "tools",
          "description": "description"
        },
        {
          "id": "new_id",
          "kind": "tools"
        }
      ]
    }
  ]
}

Hi @Jon_Madden,

Ok I se the issue, with this document structure and array nesting you cannot update and push to the same array in a single operation. BTW, there is no need to do an elemMatch if you want to find just one field and you can reference it using “.”.

You will have to seperate those into 2:

  1. Update the description
db.updateTest.updateOne({"userJourneyEvents.id" : 'eid'},[{$set : { 'userJourneyEvents.$[event].touchpoints.$[touchpoints].description' : 'description' }
 }],{ arrayFilters: [ { "event.id": "eid" } , { "touchpoints": "tpid0" } ]});
  1. Push new element.
db.updateTest.updateOne({"userJourneyEvents.id" : 'eid'},{
 $push : {'userJourneyEvents.$[event].touchpoints':{
      '$each': [
        {
          id: 'new_id',
          kind: 'tools',
          description: ''
        }
      ],
      '$sort': { kind: 1 }
    }}},{ arrayFilters: [ { "event.id": "eid" } ]});

If you need ACID consistency on those you should use transactions to perform them in a single transaction.

Another option is to build the array for this document on the client side and update the entire array all together:

db.updateTest.updateOne({"userJourneyEvents.id" : 'eid'},{$set : { 'userJourneyEvents.$[event].touchpoints' :
[
        {
          "id": "tpid0",
          "kind": "tools",
          "description": "description"
        },
        {
          "id": "new_id",
          "kind": "tools"
        }
      ]
}},{ arrayFilters: [ { "event.id": "eid" } ]});

I thought on trying aggregation pipeline updates with $zip and $map but it super complex and not worth the effort.

Please let me know if you have any additional questions.

Best regards,
Pavel

Thanks @Pavel_Duchovny! I’ll go with a multi-request transaction until/unless it becomes a performance bottleneck. Appreciate the tip on $elemMatch.

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.