Home > OS >  Removing an element in a mongoDB array based on the position of element with dynamically defined arr
Removing an element in a mongoDB array based on the position of element with dynamically defined arr

Time:06-07

My question is a combination of:

This question: Removing the array element in mongoDB based on the position of element

And this question: mongodb set object where key value dynamically changes

I know you can define the field (array) dynamically like this:

{ [arrayName]: { <condition> } }

But, I then want to remove a certain element in a dynamically defined array by specifying the position (which is also defined dynamically). In other words, the function that processes this query is coming in which two parameters: the array's name and the index of the element to remove.

The options given by the selected answer were the following:

Option 1, does not work (in general), adapted to my case this looks like:

{ $pull : { [arrayName] : { $gt: index-1, $lt: index 1 } } }

Option 2, I cannot use dynamically defined values in field selectors with quotation marks (as far as I am aware):

{ $pull : "[arrayName].[index]" } 

or

{ $pull : "[arrayName].$": index }

Option 3, is different method but can't use it for the same reason:

{ $unset: { "[arrayName].[index]": 1 } } // Won't work
{ $pull: { [arrayName]: null } } // Would probably work

The only workarounds I can think of right now involve significantly changing the design which would be a shame. Any help is appreciated!

PS: I'm using mongoose as a driver on the latest version as of today (v6.3.5) and MongoDB version 5.0.8

CodePudding user response:

On Mongo version 4.2 You can use pipelined updates to achieve this, you can get it done in multiple ways, here is what I consider the easiest two ways:

  1. using $slice and $concatArrays to remove a certain element:
db.collection.update({},
[
  {
    $set: {
      [arrayName]: {
        $concatArrays: [
          {
            $slice: [
              `$${arrayName}`,
              index,
            ]
          },
          {
            $slice: [
              `$${arrayName}`,
              index   1,
              {
                $size: `$${arrayName}`
              }
            ]
          }
        ]
      }
    }
  }
])

Mongo Playground

  1. using $filter and $zip to filter out based on index:
db.collection.updateOne(
{},
[
  {
    "$set": {
      [arrayName]: {
        $map: {
          input: {
            $filter: {
              input: {
                $zip: {
                  inputs: [
                    {
                      $range: [
                        0,
                        {
                          $size: `$${arrayName}`
                        }
                      ]
                    },
                    `$${arrayName}`
                  ]
                }
              },
              cond: {
                $ne: [
                  {
                    "$arrayElemAt": [
                      "$$this",
                      0
                    ]
                  },
                  index
                ]
              }
            }
          },
          in: {
            $arrayElemAt: [
              "$$this",
              1
            ]
          }
        }
      }
    }
  }
])

Alternatively you can just prepare

  • Related