Robert Ricketts
Robert Ricketts

Reputation: 43

Move an element from one array to another within same document MongoDB

I have data that looks like this:

{
  "_id": ObjectId("4d525ab2924f0000000022ad"),
  "array": [
    { id: 1, other: 23 },
    { id: 2, other: 21 },
    { id: 0, other: 235 },
    { id: 3, other: 765 }
  ],
  "zeroes": []
}

I'm would like to to $pull an element from one array and $push it to a second array within the same document to result in something that looks like this:

{
  "_id": ObjectId("id"), 
  "array": [
    { id: 1, other: 23 },
    { id: 2, other: 21 },
    { id: 3, other: 765 }
  ],
  "zeroes": [
    { id: 0, other: 235 }
  ]
}

I realize that I can do this by doing a find and then an update, i.e.

db.foo.findOne({"_id": param._id})
.then((doc)=>{
  db.foo.update(
    {
      "_id": param._id
    },
    {
      "$pull": {"array": {id: 0}},
      "$push": {"zeroes": {doc.array[2]} }
    }
  )
})

I was wondering if there's an atomic function that I can do this with. Something like,

db.foo.update({"_id": param._id}, {"$move": [{"array": {id: 0}}, {"zeroes": 1}]}

Found this post that generously provided the data I used, but the question remains unsolved after 4 years. Has a solution to this been crafted in the past 4 years?

Move elements from $pull to another array

Upvotes: 3

Views: 3007

Answers (1)

Pete Garafano
Pete Garafano

Reputation: 4913

There is no $move in MongoDB. That being said, the easiest solution is a 2 phase approach:

  1. Query the document
  2. Craft the update with a $pull and $push/$addToSet

The important part here, to make sure everything is idempotent, is to include the original array document in the query for the update.

Given a document of the following form:

{
    _id: "foo",
    arrayField: [
        {
            a: 1,
            b: 1
        },
        {
            a: 2,
            b: 1
        }
    ]
}

Lets say you want to move { a: 1, b: 1 } to a different field, maybe called someOtherArrayField, you would want to do something like.

var doc = db.col.findOne({_id: "foo"});
var arrayDocToMove = doc.arrayField[0];
db.col.update({_id: "foo", arrayField: { $elemMatch: arrayDocToMove} }, { $pull: { arrayField: arrayDocToMove }, $addToSet: { someOtherArrayField: arrayDocToMove } })

The reason we use the $elemMatch is to be sure the field we are about to remove from the array hasn't changed since we first queried the document. When coupled with a $pull it also isn't strictly necessary, but I am typically overly cautious in these situations. If there is no parallelism in your application, and you only have one application instance, it isn't strictly necessary.

Now when we check the resulting document, we get:

db.col.findOne()
{
        "_id" : "foo",
        "arrayField" : [
                {
                        "a" : 2,
                        "b" : 1
                }
        ],
        "someOtherArrayField" : [
                {
                        "a" : 1,
                        "b" : 1
                }
        ]
}

Upvotes: 3

Related Questions