2

I'm saving many items per User in my db via mongoose, items end inside item (array) in the User schema. How do I tell mongodb to findOneAndUpdate while keeping the items that are already there? Because right now the update basically override the whole item array in the db for that User.

What I have is this:

            var filter = { 'id': thisId };
            let update = { 
              'item': itemsArray,
              'updatedAt': Date.now()
            };

            User.findOneAndUpdate(filter, update, {upsert: true, new: true}, function(err, doc) {
                if (err) return res.send(500, {error: err});
                return;
            });

I've read about the $addToSet in the mongodb docs, but is it what I need here? How can I apply it to my query?

UPDATE: the $set as suggested below doesn't work. My example: I fetch 400 items inside the field 'item', then with a conditional based on last update date, I fetch 50 items the second time, now let's say among these 50 there's one item which is not already present among the 400 previously added, I'm expecting it to add the item to the 400 becoming 401. Instead what happens is i now find 50 items total inside the 'item' field

SOLUTION I FOUND:

User.findOneAndUpdate(filter, { $set : {'updatedAt': Date.now()}, $addToSet : {'item': itemsArray} }, {upsert: true}, function(err, doc) {
                    if (err) return res.send(500, {error: err});
                    return;
                });
m4tt
  • 825
  • 1
  • 11
  • 28

3 Answers3

2

you should use $push :

var filter = { 'id': thisId };

User.findOneAndUpdate(filter, { 
   $set : { 'updatedAt': Date.now() }, 
   $push : { 'item': itemsArray } 
}, {upsert: true, new: true}, function(err, doc) {
   if (err) return res.send(500, {error: err});
   return;
});
Amir BenAmara
  • 676
  • 7
  • 12
  • No I had the chance to test it properly now and it doesn't work. – m4tt Jan 02 '21 at 18:10
  • can you console.log(itemsArray)? – Amir BenAmara Jan 02 '21 at 20:19
  • first time has 400 strings second time has 50. first time on mongodb shows 400 correctly inserted values for field item, second time it shows 50 – m4tt Jan 02 '21 at 20:28
  • ok , so you should $push , i will edit the post – Amir BenAmara Jan 02 '21 at 20:33
  • man I have the feeling you going by tries here.. push is definitely not what is needed here, in fact it simply adds 50 to the 400 every time – m4tt Jan 02 '21 at 21:50
  • So you should edit our post , you should express more your problem , we didn't understand what you want to do here , provide us your schema , and the data you want to be stored in the DB – Amir BenAmara Jan 03 '21 at 09:40
  • The problem is very clear, if you read the UPDATE I even explain my case, the fact that you are suggesting a $push and you have 2 upvotes makes me really wonder about the veridicity of them. Anyway I was able to solve it myself, I edited with the solution days ago – m4tt Jan 09 '21 at 21:45
0

Use $addToSet with $each to update the array and use $set to update the rest of the fields. This can be done in the same query.

In this case it will perform the upsert operation since document with name raj in not present.


user = { _id: ObjectID("5ff171ce57fd21194c9f0d71"),
  name: 'ankit',
  item: [ 'apple', 'orange' ] }

db.user.findOneAndUpdate({name:'raj'},{$set:{age:13},$addToSet:{item:{$each:['apple','banana']}}},{upsert:true,new:true})

Here it will simply update the fields including the array field.


user = { _id: ObjectID("5ff171ce57fd21194c9f0d71"),
  name: 'ankit',
  item: [ 'apple', 'orange' ] }

db.user.findOneAndUpdate({name:'ankit'},{$set:{age:1},$addToSet:{item:{$each:['apple','banana']}}},{upsert:true,new:true})
  • Regarding your first example at the top, I'd use $addToSet if I had only the array to update, but I have other fields to update/create (via upsert). So I guess I can't apply $addToSet to the whole "update" variable in my case, am I correct? – m4tt Jan 02 '21 at 20:32
  • (it's an array of strings btw) Should I make two separate updates, one with only the array using $addToSet and the other one with the rest of the fields? is it supposed to be this way? or is something like `findOneAndUpdate(filter, { $set : { 'item': itemsArray }, update }, {upsert: true}, function(err, doc) {` even possible? – m4tt Jan 02 '21 at 20:39
  • You can update the array and other fields in the same query. ```User.findOneAndUpdate(filter, { $set : update, $addToSet : {item:itemObj} } , {upsert: true, new: true}, function(err, doc) { if (err) return res.send(500, {error: err}); return; })``` – ANKIT MISHRA Jan 03 '21 at 07:18
  • @m4tt Please check whether this solves your problem or not – ANKIT MISHRA Jan 06 '21 at 14:54
0

I think it's not best using the findOneAndUpdate() try this:

const filter = { 'id': thisId };
const user = await User.findOne(filter);
user.items.push(...itemsArray) // If itemsArray is an array
await user.save();
Divin Irakiza
  • 317
  • 1
  • 7