11

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.

Adam Thompson
  • 3,278
  • 3
  • 22
  • 37
  • 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. – Blakes Seven Mar 05 '16 at 06:54
  • Thank you for the feedback, I updated my question. Please let me know if I can clarify further. – Adam Thompson Mar 05 '16 at 07: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. – Blakes Seven Mar 05 '16 at 07: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. – Adam Thompson Mar 05 '16 at 07:18
  • http://stackoverflow.com/questions/872310/javascript-swap-array-elements – Shanoor Mar 05 '16 at 07:37
  • Well the big "ouch" here is that I at least hope your "client" has the whole array that you want to re-order, correct? And the "safe" way to do this is going to be by matching the specific content and "pulling" the item from the array and then "re-inserting" item into the array at the wanted position. The "unsafe" way would be to "assume" index positions and efficively overwrite the array. But the approach would depend on the data available to the client side. – Blakes Seven Mar 05 '16 at 07:39
  • @ShanShan That's a little bit "JavaScript oversimplified", and not the best way to approach updating a database. – Blakes Seven Mar 05 '16 at 07:39

1 Answers1

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.

Jonathan Muller
  • 7,348
  • 2
  • 23
  • 31
  • 1
    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? – Adam Thompson Mar 05 '16 at 18:19
  • 1
    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 – Jonathan Muller Mar 05 '16 at 18:20
  • 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? – Adam Thompson Mar 05 '16 at 18:22
  • 4
    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 – Jonathan Muller Mar 05 '16 at 18:24