1

I have got an array of objects in MongoDB and I was moving a particular element id (i.e 1) from its position to below element having id (i.e 2). So that we can get element with id as 2 -> 1 -> 3.

const arr = [
  {
    id: 1,
    name: 'foo'
  },
  {
    id: 2,
    name: 'bar'
  },
  {
    id: 3,
    name: 'zoo'
  }
]

What I've done is used $pull and $push but it gives ConflictingUpdateOperators and I don't know how to deal with it.

updatedPlan = await Plan.findOneAndUpdate(
        { _id: req.params.id },
        {
          $pull: {
                    "arr": {
                        id: 1
                    }
          },
          $push: {
                  "arr" : {
                    $each: [{ id: 1, name: 'foo'}],
                    $position: 1
                  }
          },
      );

1 Answer 1

1

In MongoDB 4.2 or newer you can update a document with Aggregation Pipeline. Using simple $map on a $range of array indexes you can shuffle these indexes and use $arrayElemAt in order to build a new array:

db.col.update({ _id: req.params.id }, [
    {
        $set: {
            arr: {
                $map: {
                    input: { $range: [ 0, { $size: "$arr" } ] },
                    in: {
                        $let: {
                            vars: {
                                newIndex: {
                                    $switch: {
                                        branches: [
                                            { case: { "$eq": [ "$$this", 0 ] }, then: 1 },
                                            { case: { "$lte": [ "$$this", 1 ] }, then: { $subtract: [ "$$this", 1 ] } },
                                        ],
                                        default: "$$this"
                                    }
                                }
                            },
                            in: {
                                $arrayElemAt: [ "$arr", "$$newIndex" ]
                            }
                        }
                    }
                }
            }
        }
    }
])
Sign up to request clarification or add additional context in comments.

8 Comments

Thank you for helping me. I understand almost everything except the $switch part, could you please help me to understand $switch.
@DeC sure, the role of swtich is to swap the indexes -> the $range operator will return [0,1,2] and you want it to be [1,0,2] since this is the final order you want to get.
So it could be simplified and look like this $map: { input: [1,0,2], in: { $arrayElemAt: [ "$arr", "$$this" ]} } if you knew from your client code that it's always 3-elements set. I assumed it can vary so $range and $switch to provide more generic pattern
what if we don't have length. I mean I just wrote a generic easy to understand code.
If you don't know length then you need to rely on $range and substitute indexes using $cond / $switch
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.