2

DB Schema

{
  "_id": "365e4101-9dda-4f21-8e8b-4bc301afcdf2",
  "isDeleted": false,
  "user": {
    "timestamp": "2023-03-04",
    "name": "test",
    "surname": "password",
    "email": "test.password@gmail.com",
  },
  "postings": [
    {
      "id": "eeb79bd8-ba20-41b5-a3fd-6377f86236fd",
      "isDeleted": false,
      "images": [
        {
          "id": "ea2209e5-63d7-47ab-8d02-60ffb67d86ca",
          "timestamp": "2023-03-18T13:59:15.630123",
          "isDeleted": false
        },
        {
          "id": "8248b0ab-968b-4109-8ebd-f7c6968051e0",
          "timestamp": "2023-03-18T13:59:17.414993",
          "isDeleted": false
        }
      ]
    },
    {
      "id": "96c04919-b5c3-4442-a593-a05e661a5ebd",
      "isDeleted": false
    },
  ]
}

Purpose

As can be seen from the schema, in the array postings, each element can have a property called images. Some elements have it, and some don't, what I am trying to do is set the fields:

  • postings.isDeleted = true // for a post specified by a post ID

  • postings.images.isDeleted = true // for every image in that post

Current Approach

update_result = await dbConnection.find_one_and_update(
        {"postings.id": id},
        [{
            "$set": {
                "postings.$[p].isDeleted": True,
                "postings.$[p].images.$[].isDeleted" : True 
                // I need a condition above to check if the field _images_ exists in the post
               // and update it if it exists, but do nothing if it does not exist
            }
        }],
        upsert=True,
        array_filters=[
            {
                "p.id": id,
                "p.isDeleted": False
            }]
    )

Current Behavior

It works fine for documents that have the images array and it deletes it correctly. But it crashes if I attempt to update the images field if it does not exist, which is logical.

Example 1

If the supplied post ID is eeb79bd8-ba20-41b5-a3fd-6377f86236fd, this is the current result:

{
      "id": "eeb79bd8-ba20-41b5-a3fd-6377f86236fd", // <- post ID
      "isDeleted": true, // <- change here
      "images": [
        {
          "id": "ea2209e5-63d7-47ab-8d02-60ffb67d86ca",
          "timestamp": "2023-03-18T13:59:15.630123",
          "isDeleted": true // <- change here
        },
        {
          "id": "8248b0ab-968b-4109-8ebd-f7c6968051e0",
          "timestamp": "2023-03-18T13:59:17.414993",
          "isDeleted": true // <- change here
        }
      ]
    }

Example 2

When the postID is 96c04919-b5c3-4442-a593-a05e661a5ebd, it crashes with the following error message:

pymongo.errors.OperationFailure: Plan executor error during findAndModify :: caused by :: The path 'postings.2.images' must exist in the document in order to apply array updates., full error: {'ok': 0.0, 'errmsg': "Plan executor error during findAndModify :: caused by :: The path 'postings.2.images' must exist in the document in order to apply array updates."

Desired Behavior

I would like to modify the above query such that:

  • It sets postings.isDeleted = True for the specified post, this is already working
  • It sets postings.images.isDeleted = True if the images array exists in the specified post

Similar Question That Didn't Help

Ahmet-Salman
  • 194
  • 8

1 Answers1

2

In your example, you can avoid the error by dividing the conditions to two different conditions. One for the posting (p) and one for the images (k). This means adding another condition to the array_filters:

{"$set": {
     "postings.$[p].isDeleted": True,
     "postings.$[k].images.$[].isDeleted" : True    
}}
array_filters=[
            {"p.id": id, "p.isDeleted": False},
            {"k.images": {$exists: true}}
]

See how it works on the playground example

nimrod serok
  • 14,151
  • 2
  • 11
  • 33