13

I need to be able to increment and decrement the position of an element of an array in a MongoDB object.

I looked at the <update> API in the MongoDB API but could not find anything to let me do so.

I am trying to use findOneAndUpdate through Mongoose and I know the index of the element I am trying to shift up or down.

An example of the array item of base64 encoded images:

{ 
  images: [
    "img1",
    "img2",
    "img3"
  ]
}

And I would like to move, for example "img2", up or down (but "image" should not be able to pushed up since there is nowhere to go).

If I wanted to push "img2" up then the result would be:

{ 
  images: [
    "img2",
    "img1",
    "img3"
  ]
}

It doesn't matter if I achieve this through changing the index, swapping, or pushing up/down.

7
  • 1
    Sorry but your question is very unclear and open to interpretation in many different ways. You should rather show a sample document and the result you want to achieve from your update. Commented Mar 5, 2016 at 6:54
  • Thank you for the feedback, I updated my question. Please let me know if I can clarify further. Commented Mar 5, 2016 at 7:00
  • 1
    The problem with writing an "abstract case" is that the answers that suit that case almost never apply to the real world scenario you are trying to represent and ultimately solve. I would show your real data, or at least as close to what really represents. It's unlikely your base64 images are as simple content as ["img1", "img2", "img3] and are of course complex encoded strings. So it's important to people to know whether or not this is just plain "base64" data directly in each array index, or whether it resides inside another document, with some identifier or key. Commented Mar 5, 2016 at 7:06
  • I agree! However, in this case it literally is just a string just as in the abstract case. And if I put in real data it will take up too much of the page for it to be useful. Commented Mar 5, 2016 at 7:18
  • stackoverflow.com/questions/872310/… Commented Mar 5, 2016 at 7:37

1 Answer 1

12

Like @blakes-seven said, you have two ways to do it:

Grabing, updating and pushing

db.images.find({ _id: '' }, { images : 1 })
.then(function(img) {
  var tmpImg = img.images[0];
  img.images[0] = img.images[1];
  img.images[1] = tmpImg;

  db.images.update({ _id: img._id }, { $set: { images: img.images } });
})

Updating directly (if you have the image on the client and know the indexes), using $position

db.images.update({ _id: '' }, { $push: { images: { $each: ['img2'] }, $position: 0 } } } )
.then(function() {
  db.images.update({ _id: '' }, {$unset: {'images.2': 1}})
});

https://docs.mongodb.org/manual/reference/operator/update/position/

However, I think you should redesign the way you stores your images, using by example a virtual ordering:

{
  images: [
    { base64: 'img1', order: 0, _id: 'img1' },
    { base64: 'img2', order: 1, _id: 'img2' },
    { base64: 'img3', order: 2, _id: 'img3' }
  ]
}

This way, you could order your images using a virtual order index, updating them using only the _id, or updating the whole collection by changing order of all img2, dropping or replacing an image, etc.

Sign up to request clarification or add additional context in comments.

5 Comments

I thought about taking your approach to adding an index to the database objects, but then wouldn't I have to update the other images order index as well, so it would largely be the same thing?
Yes you will have to update existing documents to have a uniform database the first time, for the rest I don't know your implementation and how you are using images, so it is your call it was just a suggestion :D
I mean even after the first time, if I want to change the order of a single item, I still have to modify the other order ids as well. I'm not seeing how that would benefit me in this scenario since I am modifying the whole object anyways. Is there an easy way to modify all the order ids that I am missing?
Yes but you don't have to send the whole images array to the database or the image for which you want to update its index, so you save a lot of bandwidth and have better performances
To round this out, if you go with virtual ordering (which imho is the only way to achieve atomic ops for re-ordering), see the answer to my question here on how to insert atomically: stackoverflow.com/questions/78830485/…

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.